summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2019-03-15 22:46:00 +0000
committerCarl Hetherington <cth@carlh.net>2019-03-17 00:24:07 +0000
commit7cc85a6cda9787796b3d191251c0653b6df67ceb (patch)
tree1375c6e148d903ad4af834d5db0c8b3dbc703047
parent3537583f15ebca59eb9e5abb85452b54183210ba (diff)
Finish initial CPL markers support.markers
-rw-r--r--src/reel.cc20
-rw-r--r--src/reel.h10
-rw-r--r--src/reel_asset.cc19
-rw-r--r--src/reel_asset.h7
-rw-r--r--src/reel_atmos_asset.cc2
-rw-r--r--src/reel_closed_caption_asset.cc2
-rw-r--r--src/reel_markers_asset.cc140
-rw-r--r--src/reel_markers_asset.h61
-rw-r--r--src/reel_picture_asset.cc2
-rw-r--r--src/reel_sound_asset.cc2
-rw-r--r--src/reel_subtitle_asset.cc2
-rw-r--r--src/wscript1
-rw-r--r--test/dcp_test.cc1
-rw-r--r--test/markers_test.cc106
-rw-r--r--test/wscript3
15 files changed, 357 insertions, 21 deletions
diff --git a/src/reel.cc b/src/reel.cc
index 4b42ec0e..bda83531 100644
--- a/src/reel.cc
+++ b/src/reel.cc
@@ -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());
}
diff --git a/src/reel.h b/src/reel.h
index fa37c840..0a3cf19a 100644
--- a/src/reel.h
+++ b/src/reel.h
@@ -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;
};
diff --git a/src/reel_asset.cc b/src/reel_asset.cc
index 22583223..caaf3eee 100644
--- a/src/reel_asset.cc
+++ b/src/reel_asset.cc
@@ -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"))
{
}
diff --git a/src/reel_asset.h b/src/reel_asset.h
index 9f024239..4092a97a 100644
--- a/src/reel_asset.h
+++ b/src/reel_asset.h
@@ -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
};
}
diff --git a/src/reel_atmos_asset.cc b/src/reel_atmos_asset.cc
index f5dbc9fa..1ce8b6ea 100644
--- a/src/reel_atmos_asset.cc
+++ b/src/reel_atmos_asset.cc
@@ -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())
{
diff --git a/src/reel_closed_caption_asset.cc b/src/reel_closed_caption_asset.cc
index f71c39f6..435c3438 100644
--- a/src/reel_closed_caption_asset.cc
+++ b/src/reel_closed_caption_asset.cc
@@ -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
index 00000000..8c5f66dd
--- /dev/null
+++ b/src/reel_markers_asset.cc
@@ -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
index 00000000..4077fbbe
--- /dev/null
+++ b/src/reel_markers_asset.h
@@ -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;
+};
+
+}
diff --git a/src/reel_picture_asset.cc b/src/reel_picture_asset.cc
index d2ba358f..5b4ced67 100644
--- a/src/reel_picture_asset.cc
+++ b/src/reel_picture_asset.cc
@@ -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 ())
diff --git a/src/reel_sound_asset.cc b/src/reel_sound_asset.cc
index ab763cb3..25259f42 100644
--- a/src/reel_sound_asset.cc
+++ b/src/reel_sound_asset.cc
@@ -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())
{
diff --git a/src/reel_subtitle_asset.cc b/src/reel_subtitle_asset.cc
index 1bb8ccca..a90513d5 100644
--- a/src/reel_subtitle_asset.cc
+++ b/src/reel_subtitle_asset.cc
@@ -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>())
{
diff --git a/src/wscript b/src/wscript
index ae881a8b..4b39a979 100644
--- a/src/wscript
+++ b/src/wscript
@@ -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
diff --git a/test/dcp_test.cc b/test/dcp_test.cc
index 2e1344c5..b6e14d25 100644
--- a/test/dcp_test.cc
+++ b/test/dcp_test.cc
@@ -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
index 00000000..813736de
--- /dev/null
+++ b/test/markers_test.cc
@@ -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)));
+}
diff --git a/test/wscript b/test/wscript
index 5838df68..53d84617 100644
--- a/test/wscript
+++ b/test/wscript
@@ -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