Finish initial CPL markers support.
authorCarl Hetherington <cth@carlh.net>
Fri, 15 Mar 2019 22:46:00 +0000 (22:46 +0000)
committerCarl Hetherington <cth@carlh.net>
Sun, 17 Mar 2019 00:24:27 +0000 (00:24 +0000)
15 files changed:
src/reel.cc
src/reel.h
src/reel_asset.cc
src/reel_asset.h
src/reel_atmos_asset.cc
src/reel_closed_caption_asset.cc
src/reel_markers_asset.cc [new file with mode: 0644]
src/reel_markers_asset.h [new file with mode: 0644]
src/reel_picture_asset.cc
src/reel_sound_asset.cc
src/reel_subtitle_asset.cc
src/wscript
test/dcp_test.cc
test/markers_test.cc [new file with mode: 0644]
test/wscript

index 4b42ec0e686cf74b83ba65be3538c668c119eead..bda83531ac8228e674ed9da679447ebc997cb157 100644 (file)
@@ -42,6 +42,7 @@
 #include "reel_stereo_picture_asset.h"
 #include "reel_sound_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"
@@ -84,6 +85,11 @@ Reel::Reel (boost::shared_ptr<const cxml::Node> node)
                _main_subtitle.reset (new ReelSubtitleAsset (main_subtitle));
        }
 
+       shared_ptr<cxml::Node> main_markers = asset_list->optional_node_child ("MainMarkers");
+       if (main_markers) {
+               _main_markers.reset (new ReelMarkersAsset (main_markers));
+       }
+
        /* XXX: it's not ideal that we silently tolerate Interop or SMPTE nodes here */
        /* XXX: not sure if Interop supports multiple closed captions */
        list<shared_ptr<cxml::Node> > closed_captions = asset_list->node_children ("MainClosedCaption");
@@ -110,6 +116,10 @@ Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
        reel->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid());
        xmlpp::Element* asset_list = reel->add_child ("AssetList");
 
+       if (_main_markers) {
+               _main_markers->write_to_cpl (asset_list, standard);
+       }
+
        if (_main_picture && dynamic_pointer_cast<ReelMonoPictureAsset> (_main_picture)) {
                /* Mono pictures come before other stuff... */
                _main_picture->write_to_cpl (asset_list, standard);
@@ -167,6 +177,10 @@ Reel::equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, NoteHand
                return false;
        }
 
+       if (_main_markers && !_main_markers->equals (other->_main_markers, opt, note)) {
+               return false;
+       }
+
        if (_closed_captions.size() != other->_closed_captions.size()) {
                return false;
        }
@@ -250,6 +264,7 @@ Reel::add (shared_ptr<ReelAsset> asset)
        shared_ptr<ReelPictureAsset> p = dynamic_pointer_cast<ReelPictureAsset> (asset);
        shared_ptr<ReelSoundAsset> so = dynamic_pointer_cast<ReelSoundAsset> (asset);
        shared_ptr<ReelSubtitleAsset> su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
+       shared_ptr<ReelMarkersAsset> m = dynamic_pointer_cast<ReelMarkersAsset> (asset);
        shared_ptr<ReelClosedCaptionAsset> c = dynamic_pointer_cast<ReelClosedCaptionAsset> (asset);
        shared_ptr<ReelAtmosAsset> a = dynamic_pointer_cast<ReelAtmosAsset> (asset);
        if (p) {
@@ -258,6 +273,8 @@ Reel::add (shared_ptr<ReelAsset> asset)
                _main_sound = so;
        } else if (su) {
                _main_subtitle = su;
+       } else if (m) {
+               _main_markers = m;
        } else if (c) {
                _closed_captions.push_back (c);
        } else if (a) {
@@ -319,6 +336,9 @@ Reel::duration () const
        if (_main_subtitle) {
                d = max (d, _main_subtitle->duration ());
        }
+       if (_main_markers) {
+               d = max (d, _main_markers->duration ());
+       }
        BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> i, _closed_captions) {
                d = max (d, i->duration());
        }
index fa37c84041bb8b22731d6e18fcffabdad3e0021a..0a3cf19ae8b6a344d7d04468957bdf29f20edd47 100644 (file)
@@ -56,11 +56,12 @@ class ReelAsset;
 class ReelPictureAsset;
 class ReelSoundAsset;
 class ReelSubtitleAsset;
+class ReelMarkersAsset;
 class ReelClosedCaptionAsset;
 class ReelAtmosAsset;
 class Content;
 
-/** @brief A reel within a DCP; the part which actually refers to picture, sound and subtitle data */
+/** @brief A reel within a DCP; the part which actually refers to picture, sound, subtitle, marker and Atmos data */
 class Reel : public Object
 {
 public:
@@ -70,11 +71,13 @@ public:
                boost::shared_ptr<ReelPictureAsset> picture,
                boost::shared_ptr<ReelSoundAsset> sound = boost::shared_ptr<ReelSoundAsset> (),
                boost::shared_ptr<ReelSubtitleAsset> subtitle = boost::shared_ptr<ReelSubtitleAsset> (),
+               boost::shared_ptr<ReelMarkersAsset> markers = boost::shared_ptr<ReelMarkersAsset> (),
                boost::shared_ptr<ReelAtmosAsset> atmos = boost::shared_ptr<ReelAtmosAsset> ()
                )
                : _main_picture (picture)
                , _main_sound (sound)
                , _main_subtitle (subtitle)
+               , _main_markers (markers)
                , _atmos (atmos)
        {}
 
@@ -92,6 +95,10 @@ public:
                return _main_subtitle;
        }
 
+       boost::shared_ptr<ReelMarkersAsset> main_markers () const {
+               return _main_markers;
+       }
+
        std::list<boost::shared_ptr<ReelClosedCaptionAsset> > closed_captions () const {
                return _closed_captions;
        }
@@ -118,6 +125,7 @@ private:
        boost::shared_ptr<ReelPictureAsset> _main_picture;
        boost::shared_ptr<ReelSoundAsset> _main_sound;
        boost::shared_ptr<ReelSubtitleAsset> _main_subtitle;
+       boost::shared_ptr<ReelMarkersAsset> _main_markers;
        std::list<boost::shared_ptr<ReelClosedCaptionAsset> > _closed_captions;
        boost::shared_ptr<ReelAtmosAsset> _atmos;
 };
index 225832239c84e9e6d280dba1e7e62d13210206a8..caaf3eee45fc14ca6f560899840c1f5ac89aa239 100644 (file)
@@ -50,31 +50,28 @@ using boost::optional;
 using namespace dcp;
 
 /** Construct a ReelAsset.
- *  @param asset Asset that this ReelAsset refers to.
+ *  @param id ID of this ReelAsset (which is that of the MXF, if there is one)
  *  @param edit_rate Edit rate for the asset.
  *  @param intrinsic_duration Intrinsic duration of this asset.
  *  @param entry_point Entry point to use in that asset.
  */
-ReelAsset::ReelAsset (shared_ptr<Asset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point)
-       : Object (asset->id ())
-       , _edit_rate (edit_rate)
+ReelAsset::ReelAsset (string id, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point)
+       : Object (id)
        , _intrinsic_duration (intrinsic_duration)
-       , _entry_point (entry_point)
        , _duration (intrinsic_duration - entry_point)
+       , _edit_rate (edit_rate)
+       , _entry_point (entry_point)
 {
-       /* default _annotation_text to the leaf name of our file */
-       if (asset->file ()) {
-               _annotation_text = asset->file()->leaf().string ();
-       }
+
 }
 
 ReelAsset::ReelAsset (shared_ptr<const cxml::Node> node)
        : Object (remove_urn_uuid (node->string_child ("Id")))
+       , _intrinsic_duration (node->number_child<int64_t> ("IntrinsicDuration"))
+       , _duration (node->number_child<int64_t> ("Duration"))
        , _annotation_text (node->optional_string_child ("AnnotationText").get_value_or (""))
        , _edit_rate (Fraction (node->string_child ("EditRate")))
-       , _intrinsic_duration (node->number_child<int64_t> ("IntrinsicDuration"))
        , _entry_point (node->number_child<int64_t> ("EntryPoint"))
-       , _duration (node->number_child<int64_t> ("Duration"))
 {
 
 }
index 9f02423903cac215e8cc7eb9e0300f9895901f46..4092a97a5c8ab5cf1ce69664ff34c632a6cac03a 100644 (file)
@@ -65,7 +65,7 @@ class Asset;
 class ReelAsset : public Object
 {
 public:
-       ReelAsset (boost::shared_ptr<Asset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point);
+       ReelAsset (std::string id, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point);
        explicit ReelAsset (boost::shared_ptr<const cxml::Node>);
 
        virtual xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const = 0;
@@ -119,12 +119,13 @@ protected:
 
        xmlpp::Node* write_to_cpl_base (xmlpp::Node* node, Standard standard, boost::optional<std::string> hash) const;
 
+       int64_t _intrinsic_duration;  ///< The &lt;IntrinsicDuration&gt; from the reel's entry for this asset
+       int64_t _duration;            ///< The &lt;Duration&gt; from the reel's entry for this asset
+
 private:
        std::string _annotation_text; ///< The &lt;AnnotationText&gt; from the reel's entry for this asset
        Fraction _edit_rate;          ///< The &lt;EditRate&gt; from the reel's entry for this asset
-       int64_t _intrinsic_duration;  ///< The &lt;IntrinsicDuration&gt; from the reel's entry for this asset
        int64_t _entry_point;         ///< The &lt;EntryPoint&gt; from the reel's entry for this asset
-       int64_t _duration;            ///< The &lt;Duration&gt; from the reel's entry for this asset
 };
 
 }
index f5dbc9fa7e33f5f0e1512dfb88f8cc4855ac52c2..1ce8b6eabd3e6e8522195d00eb1a35b1e0c5c5b3 100644 (file)
@@ -47,7 +47,7 @@ using boost::shared_ptr;
 using namespace dcp;
 
 ReelAtmosAsset::ReelAtmosAsset (boost::shared_ptr<AtmosAsset> asset, int64_t entry_point)
-       : ReelAsset (asset, asset->edit_rate(), asset->intrinsic_duration(), entry_point)
+       : ReelAsset (asset->id(), asset->edit_rate(), asset->intrinsic_duration(), entry_point)
        , ReelMXF (asset, asset->key_id())
 {
 
index f71c39f62066e03ee4225f09dfbfedfb15df6679..435c3438c0cb3e45fff5fab6d3515b8719a3b43f 100644 (file)
@@ -50,7 +50,7 @@ using boost::optional;
 using namespace dcp;
 
 ReelClosedCaptionAsset::ReelClosedCaptionAsset (boost::shared_ptr<SubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point)
-       : ReelAsset (asset, edit_rate, intrinsic_duration, entry_point)
+       : ReelAsset (asset->id(), edit_rate, intrinsic_duration, entry_point)
        , ReelMXF (asset, dynamic_pointer_cast<SMPTESubtitleAsset>(asset) ? dynamic_pointer_cast<SMPTESubtitleAsset>(asset)->key_id() : optional<string>())
 {
 
diff --git a/src/reel_markers_asset.cc b/src/reel_markers_asset.cc
new file mode 100644 (file)
index 0000000..8c5f66d
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+    Copyright (C) 2019 Carl Hetherington <cth@carlh.net>
+
+    This file is part of libdcp.
+
+    libdcp is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    libdcp is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of portions of this program with the
+    OpenSSL library under certain conditions as described in each
+    individual source file, and distribute linked combinations
+    including the two.
+
+    You must obey the GNU General Public License in all respects
+    for all of the code used other than OpenSSL.  If you modify
+    file(s) with this exception, you may extend this exception to your
+    version of the file(s), but you are not obligated to do so.  If you
+    do not wish to do so, delete this exception statement from your
+    version.  If you delete this exception statement from all source
+    files in the program, then also delete it here.
+*/
+
+#include "reel_markers_asset.h"
+#include "raw_convert.h"
+#include "dcp_assert.h"
+#include <libxml++/libxml++.h>
+#include <boost/foreach.hpp>
+
+using std::string;
+using std::map;
+using std::max;
+using boost::optional;
+using boost::shared_ptr;
+using namespace dcp;
+
+ReelMarkersAsset::ReelMarkersAsset (Fraction edit_rate, int64_t entry_point)
+       : ReelAsset (make_uuid(), edit_rate, 0, entry_point)
+{
+
+}
+
+ReelMarkersAsset::ReelMarkersAsset (cxml::ConstNodePtr node)
+       : ReelAsset (node)
+{
+       cxml::ConstNodePtr list = node->node_child ("MarkerList");
+       DCP_ASSERT (list);
+       BOOST_FOREACH (cxml::ConstNodePtr i, list->node_children("Marker")) {
+               set (marker_from_string(i->string_child("Label")), dcp::Time(i->number_child<int64_t>("Offset"), edit_rate().as_float(), edit_rate().numerator));
+       }
+}
+
+string
+ReelMarkersAsset::cpl_node_name (Standard) const
+{
+       return "MainMarkers";
+}
+
+void
+ReelMarkersAsset::set (Marker m, Time t)
+{
+       _markers[m] = t;
+       update_duration ();
+}
+
+void
+ReelMarkersAsset::unset (Marker m)
+{
+       _markers.erase (m);
+       update_duration ();
+}
+
+optional<Time>
+ReelMarkersAsset::get (Marker m) const
+{
+       map<Marker, Time>::const_iterator i = _markers.find (m);
+       if (i == _markers.end ()) {
+               return optional<Time>();
+       }
+       return i->second;
+}
+
+void
+ReelMarkersAsset::update_duration ()
+{
+       int const tcr = edit_rate().numerator / edit_rate().denominator;
+       _intrinsic_duration = 0;
+       for (map<Marker, Time>::const_iterator i = _markers.begin(); i != _markers.end(); ++i) {
+               _intrinsic_duration = max(_intrinsic_duration, i->second.as_editable_units(tcr));
+       }
+       _duration = _intrinsic_duration;
+}
+
+xmlpp::Node*
+ReelMarkersAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const
+{
+       int const tcr = edit_rate().numerator / edit_rate().denominator;
+       xmlpp::Node* asset = write_to_cpl_base (node, standard, optional<string>());
+       xmlpp::Node* ml = asset->add_child("MarkerList");
+       for (map<Marker, Time>::const_iterator i = _markers.begin(); i != _markers.end(); ++i) {
+               xmlpp::Node* m = ml->add_child("Marker");
+               m->add_child("Label")->add_child_text (marker_to_string(i->first));
+               m->add_child("Offset")->add_child_text (raw_convert<string>(i->second.as_editable_units(tcr)));
+       }
+
+       return asset;
+}
+
+bool
+ReelMarkersAsset::equals (shared_ptr<const ReelMarkersAsset> other, EqualityOptions opt, NoteHandler note) const
+{
+       if (!asset_equals(other, opt, note)) {
+               return false;
+       }
+
+       if (get(FFOC) != other->get(FFOC) ||
+           get(LFOC) != other->get(LFOC) ||
+           get(FFTC) != other->get(FFTC) ||
+           get(LFTC) != other->get(LFTC) ||
+           get(FFOI) != other->get(FFOI) ||
+           get(LFOI) != other->get(LFOI) ||
+           get(FFEC) != other->get(FFEC) ||
+           get(LFEC) != other->get(LFEC) ||
+           get(FFMC) != other->get(FFMC) ||
+           get(LFMC) != other->get(LFMC)) {
+               return false;
+       }
+
+       return true;
+}
diff --git a/src/reel_markers_asset.h b/src/reel_markers_asset.h
new file mode 100644 (file)
index 0000000..4077fbb
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+    Copyright (C) 2019 Carl Hetherington <cth@carlh.net>
+
+    This file is part of libdcp.
+
+    libdcp is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    libdcp is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of portions of this program with the
+    OpenSSL library under certain conditions as described in each
+    individual source file, and distribute linked combinations
+    including the two.
+
+    You must obey the GNU General Public License in all respects
+    for all of the code used other than OpenSSL.  If you modify
+    file(s) with this exception, you may extend this exception to your
+    version of the file(s), but you are not obligated to do so.  If you
+    do not wish to do so, delete this exception statement from your
+    version.  If you delete this exception statement from all source
+    files in the program, then also delete it here.
+*/
+
+#include "reel_asset.h"
+#include "dcp_time.h"
+
+namespace dcp {
+
+class ReelMarkersAsset : public ReelAsset
+{
+public:
+       ReelMarkersAsset (Fraction edit_rate, int64_t entry_point);
+       explicit ReelMarkersAsset (boost::shared_ptr<const cxml::Node>);
+
+       xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const;
+       bool equals (boost::shared_ptr<const ReelMarkersAsset>, EqualityOptions, NoteHandler) const;
+
+       void set (Marker, Time);
+       void unset (Marker);
+       boost::optional<Time> get (Marker m) const;
+
+protected:
+       std::string cpl_node_name (Standard) const;
+
+private:
+       void update_duration ();
+
+       std::map<Marker, Time> _markers;
+};
+
+}
index d2ba358f006710266feb170170d510ed28ae369a..5b4ced6735cf15037f68ae1f8b620f973a21429a 100644 (file)
@@ -53,7 +53,7 @@ using boost::optional;
 using namespace dcp;
 
 ReelPictureAsset::ReelPictureAsset (shared_ptr<PictureAsset> asset, int64_t entry_point)
-       : ReelAsset (asset, asset->edit_rate(), asset->intrinsic_duration(), entry_point)
+       : ReelAsset (asset->id(), asset->edit_rate(), asset->intrinsic_duration(), entry_point)
        , ReelMXF (asset, asset->key_id())
        , _frame_rate (asset->frame_rate ())
        , _screen_aspect_ratio (asset->screen_aspect_ratio ())
index ab763cb33f3b3afc69469c5ba085b582888adc2c..25259f427c6c596d6e974857826a05e2b9871907 100644 (file)
@@ -45,7 +45,7 @@ using boost::shared_ptr;
 using namespace dcp;
 
 ReelSoundAsset::ReelSoundAsset (shared_ptr<SoundAsset> asset, int64_t entry_point)
-       : ReelAsset (asset, asset->edit_rate(), asset->intrinsic_duration(), entry_point)
+       : ReelAsset (asset->id(), asset->edit_rate(), asset->intrinsic_duration(), entry_point)
        , ReelMXF (asset, asset->key_id())
 {
 
index 1bb8cccac7bfbceab43651c71c531e604d911b57..a90513d5c1fed7aede33b306526a85bb181aa735 100644 (file)
@@ -47,7 +47,7 @@ using boost::optional;
 using namespace dcp;
 
 ReelSubtitleAsset::ReelSubtitleAsset (boost::shared_ptr<SubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point)
-       : ReelAsset (asset, edit_rate, intrinsic_duration, entry_point)
+       : ReelAsset (asset->id(), edit_rate, intrinsic_duration, entry_point)
        , ReelMXF (asset, dynamic_pointer_cast<SMPTESubtitleAsset>(asset) ? dynamic_pointer_cast<SMPTESubtitleAsset>(asset)->key_id() : optional<string>())
 {
 
index ae881a8bf6abd25554809eb7d09e2beff593bec4..4b39a979dd1cd19774b08edaaefb8ab1bc0fdd08 100644 (file)
@@ -81,6 +81,7 @@ def build(bld):
              reel_mono_picture_asset.cc
              reel_mxf.cc
              reel_picture_asset.cc
+             reel_markers_asset.cc
              reel_sound_asset.cc
              reel_stereo_picture_asset.cc
              reel_subtitle_asset.cc
index 2e1344c51fa1f1b32ed657d565d9014751ba1162..b6e14d2529cfc0d3e688180be25d303a74ad1490 100644 (file)
@@ -281,6 +281,7 @@ BOOST_AUTO_TEST_CASE (dcp_test5)
                                  shared_ptr<dcp::ReelMonoPictureAsset> (new dcp::ReelMonoPictureAsset (mp, 0)),
                                  shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0)),
                                  shared_ptr<dcp::ReelSubtitleAsset> (),
+                                 shared_ptr<dcp::ReelMarkersAsset> (),
                                  shared_ptr<dcp::ReelAtmosAsset> (new dcp::ReelAtmosAsset (am, 0))
                                  )
                          ));
diff --git a/test/markers_test.cc b/test/markers_test.cc
new file mode 100644 (file)
index 0000000..813736d
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+    Copyright (C) 2019 Carl Hetherington <cth@carlh.net>
+
+    This file is part of libdcp.
+
+    libdcp is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    libdcp is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of portions of this program with the
+    OpenSSL library under certain conditions as described in each
+    individual source file, and distribute linked combinations
+    including the two.
+
+    You must obey the GNU General Public License in all respects
+    for all of the code used other than OpenSSL.  If you modify
+    file(s) with this exception, you may extend this exception to your
+    version of the file(s), but you are not obligated to do so.  If you
+    do not wish to do so, delete this exception statement from your
+    version.  If you delete this exception statement from all source
+    files in the program, then also delete it here.
+*/
+
+#include "cpl.h"
+#include "reel.h"
+#include "reel_markers_asset.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/test/unit_test.hpp>
+
+using std::string;
+using boost::shared_ptr;
+
+BOOST_AUTO_TEST_CASE (markers_write_test)
+{
+       dcp::CPL cpl("Markers test", dcp::TEST);
+
+       shared_ptr<dcp::ReelMarkersAsset> asset (new dcp::ReelMarkersAsset(dcp::Fraction(24, 1), 0));
+       asset->set (dcp::FFOC, dcp::Time(1, 1, 9, 16, 24));
+       asset->set (dcp::LFOC, dcp::Time(2, 5, 3, 0, 24));
+       asset->set (dcp::FFTC, dcp::Time(0, 6, 4, 2, 24));
+       asset->set (dcp::LFTC, dcp::Time(0, 6, 4, 18, 24));
+       asset->set (dcp::FFOI, dcp::Time(3, 6, 4, 18, 24));
+       asset->set (dcp::LFOI, dcp::Time(3, 2, 4, 18, 24));
+       asset->set (dcp::FFEC, dcp::Time(3, 2, 7, 18, 24));
+       asset->set (dcp::LFEC, dcp::Time(3, 2, 8, 18, 24));
+       asset->set (dcp::FFMC, dcp::Time(4, 2, 8, 18, 24));
+       asset->set (dcp::LFMC, dcp::Time(4, 3, 8, 18, 24));
+
+       shared_ptr<dcp::Reel> reel (new dcp::Reel());
+       reel->add (asset);
+
+       cpl.add (reel);
+
+       cpl.write_xml ("build/test/markers_test.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
+}
+
+static void
+note_handler (dcp::NoteType, string)
+{
+
+}
+
+BOOST_AUTO_TEST_CASE (markers_read_test, * boost::unit_test::depends_on("markers_write_test"))
+{
+       dcp::CPL cpl ("build/test/markers_test.xml");
+       BOOST_CHECK_EQUAL (cpl.reels().size(), 1);
+       shared_ptr<dcp::Reel> reel = cpl.reels().front();
+       shared_ptr<dcp::ReelMarkersAsset> markers = reel->main_markers ();
+       BOOST_REQUIRE (markers);
+
+       BOOST_REQUIRE (markers->get(dcp::FFOC));
+       BOOST_CHECK (markers->get(dcp::FFOC) == dcp::Time(1, 1, 9, 16, 24));
+       BOOST_REQUIRE (markers->get(dcp::LFOC));
+       BOOST_CHECK (markers->get(dcp::LFOC) == dcp::Time(2, 5, 3, 0, 24));
+       BOOST_REQUIRE (markers->get(dcp::FFTC));
+       BOOST_CHECK (markers->get (dcp::FFTC) == dcp::Time(0, 6, 4, 2, 24));
+       BOOST_REQUIRE (markers->get(dcp::LFTC));
+       BOOST_CHECK (markers->get (dcp::LFTC) == dcp::Time(0, 6, 4, 18, 24));
+       BOOST_REQUIRE (markers->get(dcp::FFOI));
+       BOOST_CHECK (markers->get (dcp::FFOI) == dcp::Time(3, 6, 4, 18, 24));
+       BOOST_REQUIRE (markers->get(dcp::LFOI));
+       BOOST_CHECK (markers->get (dcp::LFOI) == dcp::Time(3, 2, 4, 18, 24));
+       BOOST_REQUIRE (markers->get(dcp::FFEC));
+       BOOST_CHECK (markers->get (dcp::FFEC) == dcp::Time(3, 2, 7, 18, 24));
+       BOOST_REQUIRE (markers->get(dcp::LFEC));
+       BOOST_CHECK (markers->get (dcp::LFEC) == dcp::Time(3, 2, 8, 18, 24));
+       BOOST_REQUIRE (markers->get(dcp::FFMC));
+       BOOST_CHECK (markers->get (dcp::FFMC) == dcp::Time(4, 2, 8, 18, 24));
+       BOOST_REQUIRE (markers->get(dcp::LFMC));
+       BOOST_CHECK (markers->get (dcp::LFMC) == dcp::Time(4, 3, 8, 18, 24));
+
+       BOOST_CHECK (markers->equals(markers, dcp::EqualityOptions(), boost::bind(&note_handler, _1, _2)));
+
+       shared_ptr<dcp::ReelMarkersAsset> markers2 (new dcp::ReelMarkersAsset(dcp::Fraction(24, 1), 0));
+       BOOST_CHECK (!markers->equals(markers2, dcp::EqualityOptions(), boost::bind(&note_handler, _1, _2)));
+}
index 5838df68bdeb88b6df2203eb2d0dada716ddb468..53d8461714e1d01a3de31320270292d943d905ab 100644 (file)
@@ -1,5 +1,5 @@
 #
-#    Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
+#    Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
 #
 #    This file is part of libdcp.
 #
@@ -80,6 +80,7 @@ def build(bld):
                  interop_load_font_test.cc
                  local_time_test.cc
                  make_digest_test.cc
+                 markers_test.cc
                  kdm_test.cc
                  raw_convert_test.cc
                  read_dcp_test.cc