return _uuid;
}
- virtual std::list<std::string> equals (boost::shared_ptr<const Asset> other, EqualityOptions opt) const = 0;
+ virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const = 0;
protected:
friend class PictureAsset;
}
-list<string>
-DCP::equals (DCP const & other, EqualityOptions opt) const
+bool
+DCP::equals (DCP const & other, EqualityOptions opt, list<string>& notes) const
{
- list<string> notes;
-
if (_cpls.size() != other._cpls.size()) {
notes.push_back ("CPL counts differ");
+ return false;
}
list<shared_ptr<const CPL> >::const_iterator a = _cpls.begin ();
list<shared_ptr<const CPL> >::const_iterator b = other._cpls.begin ();
while (a != _cpls.end ()) {
- list<string> n = (*a)->equals (*b->get(), opt);
- notes.merge (n);
+ if (!(*a)->equals (*b->get(), opt, notes)) {
+ return false;
+ }
++a;
++b;
}
- return notes;
+ return true;
}
-list<string>
-CPL::equals (CPL const & other, EqualityOptions opt) const
+bool
+CPL::equals (CPL const & other, EqualityOptions opt, list<string>& notes) const
{
- list<string> notes;
-
- if (opt.flags & LIBDCP_METADATA) {
- if (_name != other._name) {
- notes.push_back ("names differ");
- }
- if (_content_kind != other._content_kind) {
- notes.push_back ("content kinds differ");
- }
- if (_fps != other._fps) {
- notes.push_back ("frames per second differ");
- }
- if (_length != other._length) {
- notes.push_back ("lengths differ");
- }
+ if (_name != other._name) {
+ notes.push_back ("names differ");
+ return false;
+ }
+
+ if (_content_kind != other._content_kind) {
+ notes.push_back ("content kinds differ");
+ return false;
+ }
+
+ if (_fps != other._fps) {
+ notes.push_back ("frames per second differ");
+ return false;
+ }
+
+ if (_length != other._length) {
+ notes.push_back ("lengths differ");
+ return false;
}
if (_reels.size() != other._reels.size()) {
notes.push_back ("reel counts differ");
+ return false;
}
list<shared_ptr<const Reel> >::const_iterator a = _reels.begin ();
list<shared_ptr<const Reel> >::const_iterator b = other._reels.begin ();
while (a != _reels.end ()) {
- list<string> n = (*a)->equals (*b, opt);
- notes.merge (n);
+ if (!(*a)->equals (*b, opt, notes)) {
+ return false;
+ }
++a;
++b;
}
- return notes;
+ return true;
}
std::list<boost::shared_ptr<const Asset> > assets () const;
- std::list<std::string> equals (CPL const & other, EqualityOptions options) const;
+ bool equals (CPL const & other, EqualityOptions options, std::list<std::string>& notes) const;
void write_xml () const;
void write_to_assetmap (std::ostream& s) const;
/** Compare this DCP with another, according to various options.
* @param other DCP to compare this one to.
* @param options Options to define just what "equality" means.
- * @return An empty list if the DCPs are equal; otherwise a list of messages
- * which explain the ways in which they differ.
+ * @param notes Filled in with notes about differences.
+ * @return true if the DCPs are equal according to EqualityOptions, otherwise false.
*/
- std::list<std::string> equals (DCP const & other, EqualityOptions options) const;
+ bool equals (DCP const & other, EqualityOptions options, std::list<std::string>& notes) const;
void add_cpl (boost::shared_ptr<CPL> cpl);
assert (c == Kumu::UUID_Length);
}
-list<string>
-MXFAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const
+bool
+MXFAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const
{
shared_ptr<const MXFAsset> other_mxf = dynamic_pointer_cast<const MXFAsset> (other);
if (!other_mxf) {
- return list<string> ();
+ notes.push_back ("comparing an MXF asset with a non-MXF asset");
+ return false;
}
- list<string> notes;
+ if (_file_name != other_mxf->_file_name) {
+ notes.push_back ("MXF names differ");
+ return false;
+ }
+
+ if (_fps != other_mxf->_fps) {
+ notes.push_back ("MXF frames per second differ");
+ return false;
+ }
- if (opt.flags & LIBDCP_METADATA) {
- if (_file_name != other_mxf->_file_name) {
- notes.push_back ("MXF names differ");
- }
- if (_fps != other_mxf->_fps) {
- notes.push_back ("MXF frames per second differ");
- }
- if (_length != other_mxf->_length) {
- notes.push_back ("MXF lengths differ");
- }
+ if (_length != other_mxf->_length) {
+ notes.push_back ("MXF lengths differ");
+ return false;
}
- if (opt.flags & MXF_BITWISE) {
+ if (opt.bitwise) {
if (digest() != other_mxf->digest()) {
notes.push_back ("MXF digests differ");
+ return false;
}
if (filesystem::file_size (path()) != filesystem::file_size (other_mxf->path())) {
notes.push_back (path().string() + " and " + other_mxf->path().string() + " sizes differ");
- return notes;
+ return false;
}
ifstream a (path().string().c_str(), ios::binary);
if (memcmp (abuffer, bbuffer, t) != 0) {
notes.push_back (path().string() + " and " + other_mxf->path().string() + " content differs");
- return notes;
+ return false;
}
n -= t;
}
}
- return notes;
+ return true;
}
int
*/
MXFAsset (std::string directory, std::string file_name, sigc::signal1<void, float>* progress, int fps, int entry_point, int length);
- virtual std::list<std::string> equals (boost::shared_ptr<const Asset> other, EqualityOptions opt) const;
+ virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const;
int length () const;
<< " </MainPicture>\n";
}
-list<string>
-PictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const
+bool
+PictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const
{
- list<string> notes = MXFAsset::equals (other, opt);
+ if (!MXFAsset::equals (other, opt, notes)) {
+ return false;
+ }
- if (opt.flags & MXF_INSPECT) {
+ if (!opt.bitwise) {
ASDCP::JP2K::MXFReader reader_A;
if (ASDCP_FAILURE (reader_A.OpenRead (path().string().c_str()))) {
throw MXFFileError ("could not open MXF file for reading", path().string());
// }
}
- return notes;
+ return true;
}
}
-list<string>
-MonoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const
+bool
+MonoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const
{
- list<string> notes = PictureAsset::equals (other, opt);
+ if (!PictureAsset::equals (other, opt, notes)) {
+ return false;
+ }
shared_ptr<const MonoPictureAsset> other_picture = dynamic_pointer_cast<const MonoPictureAsset> (other);
assert (other_picture);
- for (int i = 0; i < _length; ++i) {
- shared_ptr<const MonoPictureFrame> frame_A = get_frame (i);
- shared_ptr<const MonoPictureFrame> frame_B = other_picture->get_frame (i);
-
- list<string> n = frame_buffer_equals (
- i, opt,
- frame_A->j2k_frame()->RoData(), frame_A->j2k_frame()->Size(),
- frame_B->j2k_frame()->RoData(), frame_B->j2k_frame()->Size()
- );
-
- notes.merge (n);
+ if (!opt.bitwise) {
+ for (int i = 0; i < _length; ++i) {
+ shared_ptr<const MonoPictureFrame> frame_A = get_frame (i);
+ shared_ptr<const MonoPictureFrame> frame_B = other_picture->get_frame (i);
+
+ if (!frame_buffer_equals (
+ i, opt, notes,
+ frame_A->j2k_frame()->RoData(), frame_A->j2k_frame()->Size(),
+ frame_B->j2k_frame()->RoData(), frame_B->j2k_frame()->Size()
+ )) {
+ return false;
+ }
+ }
}
- return notes;
+ return true;
}
-list<string>
-StereoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const
+bool
+StereoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const
{
- list<string> notes = PictureAsset::equals (other, opt);
+ if (!PictureAsset::equals (other, opt, notes)) {
+ return false;
+ }
shared_ptr<const StereoPictureAsset> other_picture = dynamic_pointer_cast<const StereoPictureAsset> (other);
assert (other_picture);
-
- for (int i = 0; i < _length; ++i) {
- shared_ptr<const StereoPictureFrame> frame_A = get_frame (i);
- shared_ptr<const StereoPictureFrame> frame_B = other_picture->get_frame (i);
-
- list<string> n = frame_buffer_equals (
- i, opt,
- frame_A->j2k_frame()->Left.RoData(), frame_A->j2k_frame()->Left.Size(),
- frame_B->j2k_frame()->Left.RoData(), frame_B->j2k_frame()->Left.Size()
- );
-
- notes.merge (n);
- n = frame_buffer_equals (
- i, opt,
- frame_A->j2k_frame()->Right.RoData(), frame_A->j2k_frame()->Right.Size(),
- frame_B->j2k_frame()->Right.RoData(), frame_B->j2k_frame()->Right.Size()
- );
+ if (!opt.bitwise) {
+
+ for (int i = 0; i < _length; ++i) {
+ shared_ptr<const StereoPictureFrame> frame_A = get_frame (i);
+ shared_ptr<const StereoPictureFrame> frame_B = other_picture->get_frame (i);
+
+ if (!frame_buffer_equals (
+ i, opt, notes,
+ frame_A->j2k_frame()->Left.RoData(), frame_A->j2k_frame()->Left.Size(),
+ frame_B->j2k_frame()->Left.RoData(), frame_B->j2k_frame()->Left.Size()
+ )) {
+ return false;
+ }
- notes.merge (n);
+ if (!frame_buffer_equals (
+ i, opt, notes,
+ frame_A->j2k_frame()->Right.RoData(), frame_A->j2k_frame()->Right.Size(),
+ frame_B->j2k_frame()->Right.RoData(), frame_B->j2k_frame()->Right.Size()
+ )) {
+ return false;
+ }
+ }
}
- return notes;
+ return true;
}
-list<string>
+bool
PictureAsset::frame_buffer_equals (
- int frame, EqualityOptions opt, uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B
+ int frame, EqualityOptions opt, list<string>& notes, uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B
) const
{
- list<string> notes;
+ if (size_A == size_B && memcmp (data_A, data_B, size_A) == 0) {
+ /* Easy result; the J2K data is identical */
+ return true;
+ }
+
+ /* Decompress the images to bitmaps */
+ opj_image_t* image_A = decompress_j2k (const_cast<uint8_t*> (data_A), size_A, 0);
+ opj_image_t* image_B = decompress_j2k (const_cast<uint8_t*> (data_B), size_B, 0);
+
+ /* Compare them */
- bool j2k_same = true;
- if (size_A != size_B) {
- notes.push_back ("sizes of video data for frame " + lexical_cast<string>(frame) + " differ");
- j2k_same = false;
- } else if (memcmp (data_A, data_B, size_A) != 0) {
- notes.push_back ("J2K data for frame " + lexical_cast<string>(frame) + " differ");
- j2k_same = false;
+ if (image_A->numcomps != image_B->numcomps) {
+ notes.push_back ("image component counts for frame " + lexical_cast<string>(frame) + " differ");
+ return false;
}
- if (!j2k_same) {
-
- if (opt.verbose) {
- cout << "J2K images for " << frame << " differ; checking by pixel\n";
- }
-
- /* Decompress the images to bitmaps */
- opj_image_t* image_A = decompress_j2k (const_cast<uint8_t*> (data_A), size_A, 0);
- opj_image_t* image_B = decompress_j2k (const_cast<uint8_t*> (data_B), size_B, 0);
-
- /* Compare them */
-
- if (image_A->numcomps != image_B->numcomps) {
- notes.push_back ("image component counts for frame " + lexical_cast<string>(frame) + " differ");
- }
-
- vector<int> abs_diffs (image_A->comps[0].w * image_A->comps[0].h * image_A->numcomps);
- int d = 0;
- int max_diff = 0;
-
- for (int c = 0; c < image_A->numcomps; ++c) {
-
- if (image_A->comps[c].w != image_B->comps[c].w || image_A->comps[c].h != image_B->comps[c].h) {
- notes.push_back ("image sizes for frame " + lexical_cast<string>(frame) + " differ");
- }
-
- int const pixels = image_A->comps[c].w * image_A->comps[c].h;
- for (int j = 0; j < pixels; ++j) {
- int const t = abs (image_A->comps[c].data[j] - image_B->comps[c].data[j]);
- abs_diffs[d++] = t;
- max_diff = max (max_diff, t);
- }
- }
-
- uint64_t total = 0;
- for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) {
- total += *j;
- }
-
- double const mean = double (total) / abs_diffs.size ();
-
- uint64_t total_squared_deviation = 0;
- for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) {
- total_squared_deviation += pow (*j - mean, 2);
- }
-
- double const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size());
+ vector<int> abs_diffs (image_A->comps[0].w * image_A->comps[0].h * image_A->numcomps);
+ int d = 0;
+ int max_diff = 0;
+
+ for (int c = 0; c < image_A->numcomps; ++c) {
- if (mean > opt.max_mean_pixel_error || std_dev > opt.max_std_dev_pixel_error) {
- notes.push_back ("mean or standard deviation out of range for " + lexical_cast<string>(frame));
+ if (image_A->comps[c].w != image_B->comps[c].w || image_A->comps[c].h != image_B->comps[c].h) {
+ notes.push_back ("image sizes for frame " + lexical_cast<string>(frame) + " differ");
+ return false;
}
- if (opt.verbose) {
- cout << "\tmax pixel error " << max_diff << ", mean pixel error " << mean << ", standard deviation " << std_dev << "\n";
+ int const pixels = image_A->comps[c].w * image_A->comps[c].h;
+ for (int j = 0; j < pixels; ++j) {
+ int const t = abs (image_A->comps[c].data[j] - image_B->comps[c].data[j]);
+ abs_diffs[d++] = t;
+ max_diff = max (max_diff, t);
}
+ }
- opj_image_destroy (image_A);
- opj_image_destroy (image_B);
+ uint64_t total = 0;
+ for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) {
+ total += *j;
+ }
+
+ double const mean = double (total) / abs_diffs.size ();
+
+ uint64_t total_squared_deviation = 0;
+ for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) {
+ total_squared_deviation += pow (*j - mean, 2);
+ }
+
+ double const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size());
+
+ if (mean > opt.max_mean_pixel_error || std_dev > opt.max_std_dev_pixel_error) {
+ notes.push_back ("mean or standard deviation out of range for " + lexical_cast<string>(frame));
+ return false;
}
+
+ opj_image_destroy (image_A);
+ opj_image_destroy (image_B);
- return notes;
+ return true;
}
*/
void write_to_cpl (std::ostream& s) const;
- std::list<std::string> equals (boost::shared_ptr<const Asset> other, EqualityOptions opt) const;
+ bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const;
int width () const {
return _width;
protected:
- std::list<std::string> frame_buffer_equals (
- int frame, EqualityOptions opt, uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B
+ bool frame_buffer_equals (
+ int frame, EqualityOptions opt, std::list<std::string>& notes,
+ uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B
) const;
/** picture width in pixels */
MonoPictureAsset (std::string directory, std::string mxf_name, int fps, int entry_point, int length);
boost::shared_ptr<const MonoPictureFrame> get_frame (int n) const;
- std::list<std::string> equals (boost::shared_ptr<const Asset> other, EqualityOptions opt) const;
+ bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const;
private:
std::string path_from_list (int f, std::vector<std::string> const & files) const;
StereoPictureAsset (std::string directory, std::string mxf_name, int fps, int entry_point, int length);
boost::shared_ptr<const StereoPictureFrame> get_frame (int n) const;
- std::list<std::string> equals (boost::shared_ptr<const Asset> other, EqualityOptions opt) const;
+ bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const;
};
}
}
-list<string>
-Reel::equals (boost::shared_ptr<const Reel> other, EqualityOptions opt) const
+bool
+Reel::equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, list<string>& notes) const
{
- list<string> o;
-
if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
- o.push_back ("reel has different assets");
+ notes.push_back ("reel has different assets");
+ return false;
}
- if (_main_picture) {
- list<string> m = _main_picture->equals (other->_main_picture, opt);
- o.merge (m);
+ if (_main_picture && !_main_picture->equals (other->_main_picture, opt, notes)) {
+ return false;
}
if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) {
- o.push_back ("reel has different assets");
+ notes.push_back ("reel has different assets");
+ return false;
}
- if (_main_sound) {
- list<string> m = _main_sound->equals (other->_main_sound, opt);
- o.merge (m);
+ if (_main_sound && !_main_sound->equals (other->_main_sound, opt, notes)) {
+ return false;
}
if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) {
- o.push_back ("reel has different assets");
+ notes.push_back ("reel has different assets");
+ return false;
}
- if (_main_subtitle) {
- list<string> m = _main_subtitle->equals (other->_main_subtitle, opt);
- o.merge (m);
+ if (_main_subtitle && !_main_subtitle->equals (other->_main_subtitle, opt, notes)) {
+ return false;
}
- return o;
+ return true;
}
void write_to_cpl (std::ostream & s) const;
- std::list<std::string> equals (boost::shared_ptr<const Reel> other, EqualityOptions opt) const;
+ bool equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, std::list<std::string>& notes) const;
private:
boost::shared_ptr<const PictureAsset> _main_picture;
<< " </MainSound>\n";
}
-list<string>
-SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt) const
+bool
+SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const
{
- list<string> notes = MXFAsset::equals (other, opt);
+ if (!MXFAsset::equals (other, opt, notes)) {
+ return false;
+ }
- if (opt.flags & MXF_INSPECT) {
+ if (!opt.bitwise) {
ASDCP::PCM::MXFReader reader_A;
if (ASDCP_FAILURE (reader_A.OpenRead (path().string().c_str()))) {
throw MXFFileError ("could not open MXF file for reading", path().string());
) {
notes.push_back ("audio MXF picture descriptors differ");
+ return false;
}
ASDCP::PCM::FrameBuffer buffer_A (1 * Kumu::Megabyte);
if (buffer_A.Size() != buffer_B.Size()) {
notes.push_back ("sizes of audio data for frame " + lexical_cast<string>(i) + " differ");
- continue;
+ return false;
}
if (memcmp (buffer_A.RoData(), buffer_B.RoData(), buffer_A.Size()) != 0) {
notes.push_back ("PCM data for MXF frame " + lexical_cast<string>(i) + " differ");
- continue;
+ return false;
}
}
}
- return notes;
+ return true;
}
shared_ptr<const SoundFrame>
*/
void write_to_cpl (std::ostream& s) const;
- std::list<std::string> equals (boost::shared_ptr<const Asset> other, EqualityOptions opt) const;
+ bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const;
boost::shared_ptr<const SoundFrame> get_frame (int n) const;
SubtitleAsset (std::string directory, std::string xml);
void write_to_cpl (std::ostream&) const {}
- virtual std::list<std::string> equals (boost::shared_ptr<const Asset>, EqualityOptions) const {
+ virtual bool equals (boost::shared_ptr<const Asset>, EqualityOptions, std::list<std::string>& notes) const {
/* XXX */
- return std::list<std::string> ();
+ notes.push_back ("subtitle assets not compared yet");
+ return true;
}
std::string language () const {
extern bool operator== (Fraction const & a, Fraction const & b);
extern bool operator!= (Fraction const & a, Fraction const & b);
-enum EqualityFlags {
- LIBDCP_METADATA = 0x1,
- MXF_BITWISE = 0x2,
- MXF_INSPECT = 0x4
-};
-
struct EqualityOptions {
- EqualityFlags flags;
- bool verbose;
+ /** true to do a bitwise comparison.
+ * false to compare PCM and image data, possibly allowing
+ * some variation in values.
+ */
+ bool bitwise;
double max_mean_pixel_error;
double max_std_dev_pixel_error;
};
cerr << "Syntax: " << n << " [OPTION] <DCP> <DCP>\n"
<< " -b, --bitwise bitwise check\n"
<< " -v, --version show libdcp version\n"
- << " -d, --verbose be verbose\n"
<< " -h, --help show this help\n"
<< "\n"
<< "The <DCP>s are the DCP directories to compare.\n"
main (int argc, char* argv[])
{
EqualityOptions options;
- options.flags = EqualityFlags (LIBDCP_METADATA | MXF_INSPECT);
- options.verbose = false;
+ options.bitwise = false;
int option_index = 0;
while (1) {
{ "bitwise", no_argument, 0, 'b'},
{ "version", no_argument, 0, 'v'},
{ "help", no_argument, 0, 'h'},
- { "verbose", no_argument, 0, 'd'},
{ 0, 0, 0, 0 }
};
switch (c) {
case 'b':
- options.flags = EqualityFlags (options.flags | MXF_BITWISE);
+ options.bitwise = true;
break;
case 'v':
cout << "dcpdiff version " << LIBDCP_VERSION << "\n";
case 'h':
help (argv[0]);
exit (EXIT_SUCCESS);
- case 'd':
- options.verbose = true;
- break;
}
}
options.max_mean_pixel_error = 5;
options.max_std_dev_pixel_error = 5;
- list<string> notes = a->equals (*b, options);
- if (notes.empty ()) {
- cout << "DCPs equal\n";
- exit (EXIT_SUCCESS);
- }
+ list<string> notes;
+ bool equals = a->equals (*b, options, notes);
for (list<string>::iterator i = notes.begin(); i != notes.end(); ++i) {
cout << " " << *i << "\n";
}
+ if (equals) {
+ cout << "DCPs equal\n";
+ exit (EXIT_SUCCESS);
+ }
+
exit (EXIT_FAILURE);
}