2 Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <boost/filesystem.hpp>
27 #include "exceptions.h"
28 #include "subtitle_asset.h"
29 #include "picture_asset.h"
30 #include "sound_asset.h"
32 #include "gamma_lut.h"
35 #define BOOST_TEST_DYN_LINK
36 #define BOOST_TEST_MODULE libdcp_test
37 #include <boost/test/unit_test.hpp>
43 using boost::shared_ptr;
48 return "test/data/32x32_red_square.j2c";
54 return "test/data/1s_24-bit_48k_silence.wav";
58 BOOST_AUTO_TEST_CASE (dcp_test)
60 Kumu::libdcp_test = true;
62 libdcp::XMLMetadata xml_meta;
63 xml_meta.issuer = "OpenDCP 0.0.25";
64 xml_meta.creator = "OpenDCP 0.0.25";
65 xml_meta.issue_date = "2012-07-17T04:45:18+00:00";
66 libdcp::MXFMetadata mxf_meta;
67 mxf_meta.company_name = "OpenDCP";
68 mxf_meta.product_name = "OpenDCP";
69 mxf_meta.product_version = "0.0.25";
70 boost::filesystem::remove_all ("build/test/foo");
71 boost::filesystem::create_directories ("build/test/foo");
72 libdcp::DCP d ("build/test/foo");
73 shared_ptr<libdcp::CPL> cpl (new libdcp::CPL ("build/test/foo", "A Test DCP", libdcp::FEATURE, 24, 24));
75 shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset (
82 libdcp::Size (32, 32),
86 shared_ptr<libdcp::SoundAsset> ms (new libdcp::SoundAsset (
97 cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (mp, ms, shared_ptr<libdcp::SubtitleAsset> ())));
100 d.write_xml (xml_meta);
103 BOOST_AUTO_TEST_CASE (error_test)
105 libdcp::DCP d ("build/test/bar");
107 p.push_back ("frobozz");
109 BOOST_CHECK_THROW (new libdcp::MonoPictureAsset (p, "build/test/bar", "video.mxf", &d.Progress, 24, 24, libdcp::Size (32, 32)), libdcp::FileError);
110 BOOST_CHECK_THROW (new libdcp::SoundAsset (p, "build/test/bar", "audio.mxf", &d.Progress, 24, 24), libdcp::FileError);
113 BOOST_AUTO_TEST_CASE (read_dcp)
115 libdcp::DCP d ("test/ref/DCP");
118 list<shared_ptr<const libdcp::CPL> > cpls = d.cpls ();
119 BOOST_CHECK_EQUAL (cpls.size(), 1);
121 BOOST_CHECK_EQUAL (cpls.front()->name(), "A Test DCP");
122 BOOST_CHECK_EQUAL (cpls.front()->content_kind(), libdcp::FEATURE);
123 BOOST_CHECK_EQUAL (cpls.front()->frames_per_second(), 24);
124 BOOST_CHECK_EQUAL (cpls.front()->length(), 24);
127 BOOST_AUTO_TEST_CASE (subtitles1)
129 libdcp::SubtitleAsset subs ("test/data", "subs1.xml");
131 BOOST_CHECK_EQUAL (subs.language(), "French");
133 list<shared_ptr<libdcp::Subtitle> > s = subs.subtitles_at (libdcp::Time (0, 0, 6, 1));
134 BOOST_CHECK_EQUAL (s.size(), 1);
135 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
138 libdcp::Color (255, 255, 255),
140 libdcp::Time (0, 0, 5, 198),
141 libdcp::Time (0, 0, 7, 115),
144 "My jacket was Idi Amin's",
146 libdcp::Color (0, 0, 0),
147 libdcp::Time (0, 0, 0, 1),
148 libdcp::Time (0, 0, 0, 1)
151 s = subs.subtitles_at (libdcp::Time (0, 0, 7, 190));
152 BOOST_CHECK_EQUAL (s.size(), 2);
153 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
156 libdcp::Color (255, 255, 255),
158 libdcp::Time (0, 0, 7, 177),
159 libdcp::Time (0, 0, 11, 31),
162 "My corset was H.M. The Queen's",
164 libdcp::Color (0, 0, 0),
165 libdcp::Time (0, 0, 0, 1),
166 libdcp::Time (0, 0, 0, 1)
168 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
171 libdcp::Color (255, 255, 255),
173 libdcp::Time (0, 0, 7, 177),
174 libdcp::Time (0, 0, 11, 31),
177 "My large wonderbra",
179 libdcp::Color (0, 0, 0),
180 libdcp::Time (0, 0, 0, 1),
181 libdcp::Time (0, 0, 0, 1)
184 s = subs.subtitles_at (libdcp::Time (0, 0, 11, 95));
185 BOOST_CHECK_EQUAL (s.size(), 1);
186 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
189 libdcp::Color (255, 255, 255),
191 libdcp::Time (0, 0, 11, 94),
192 libdcp::Time (0, 0, 13, 63),
195 "Once belonged to the Shah",
197 libdcp::Color (0, 0, 0),
198 libdcp::Time (0, 0, 0, 1),
199 libdcp::Time (0, 0, 0, 1)
202 s = subs.subtitles_at (libdcp::Time (0, 0, 14, 42));
203 BOOST_CHECK_EQUAL (s.size(), 1);
204 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
207 libdcp::Color (255, 255, 255),
209 libdcp::Time (0, 0, 13, 104),
210 libdcp::Time (0, 0, 15, 177),
213 "And these are Roy Hattersley's jeans",
215 libdcp::Color (0, 0, 0),
216 libdcp::Time (0, 0, 0, 1),
217 libdcp::Time (0, 0, 0, 1)
221 BOOST_AUTO_TEST_CASE (subtitles2)
223 libdcp::SubtitleAsset subs ("test/data", "subs2.xml");
225 list<shared_ptr<libdcp::Subtitle> > s = subs.subtitles_at (libdcp::Time (0, 0, 42, 100));
226 BOOST_CHECK_EQUAL (s.size(), 2);
227 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
230 libdcp::Color (255, 255, 255),
232 libdcp::Time (0, 0, 41, 62),
233 libdcp::Time (0, 0, 43, 52),
236 "At afternoon tea with John Peel",
238 libdcp::Color (0, 0, 0),
239 libdcp::Time (0, 0, 0, 0),
240 libdcp::Time (0, 0, 0, 0)
242 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
245 libdcp::Color (255, 255, 255),
247 libdcp::Time (0, 0, 41, 62),
248 libdcp::Time (0, 0, 43, 52),
251 "I enquired if his accent was real",
253 libdcp::Color (0, 0, 0),
254 libdcp::Time (0, 0, 0, 0),
255 libdcp::Time (0, 0, 0, 0)
258 s = subs.subtitles_at (libdcp::Time (0, 0, 50, 50));
259 BOOST_CHECK_EQUAL (s.size(), 2);
260 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
263 libdcp::Color (255, 255, 255),
265 libdcp::Time (0, 0, 50, 42),
266 libdcp::Time (0, 0, 52, 21),
269 "He said \"out of the house",
271 libdcp::Color (0, 0, 0),
272 libdcp::Time (0, 0, 0, 0),
273 libdcp::Time (0, 0, 0, 0)
275 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
278 libdcp::Color (255, 255, 255),
280 libdcp::Time (0, 0, 50, 42),
281 libdcp::Time (0, 0, 52, 21),
284 "I'm incredibly scouse",
286 libdcp::Color (0, 0, 0),
287 libdcp::Time (0, 0, 0, 0),
288 libdcp::Time (0, 0, 0, 0)
291 s = subs.subtitles_at (libdcp::Time (0, 1, 2, 300));
292 BOOST_CHECK_EQUAL (s.size(), 2);
293 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
296 libdcp::Color (255, 255, 255),
298 libdcp::Time (0, 1, 2, 208),
299 libdcp::Time (0, 1, 4, 10),
302 "At home it depends how I feel.\"",
304 libdcp::Color (0, 0, 0),
305 libdcp::Time (0, 0, 0, 0),
306 libdcp::Time (0, 0, 0, 0)
308 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
311 libdcp::Color (255, 255, 255),
313 libdcp::Time (0, 1, 2, 208),
314 libdcp::Time (0, 1, 4, 10),
317 "I spent a long weekend in Brighton",
319 libdcp::Color (0, 0, 0),
320 libdcp::Time (0, 0, 0, 0),
321 libdcp::Time (0, 0, 0, 0)
324 s = subs.subtitles_at (libdcp::Time (0, 1, 15, 50));
325 BOOST_CHECK_EQUAL (s.size(), 2);
326 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
329 libdcp::Color (255, 255, 255),
331 libdcp::Time (0, 1, 15, 42),
332 libdcp::Time (0, 1, 16, 42),
335 "With the legendary Miss Enid Blyton",
337 libdcp::Color (0, 0, 0),
338 libdcp::Time (0, 0, 0, 0),
339 libdcp::Time (0, 0, 0, 0)
341 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
344 libdcp::Color (255, 255, 255),
346 libdcp::Time (0, 1, 15, 42),
347 libdcp::Time (0, 1, 16, 42),
350 "She said \"you be Noddy",
352 libdcp::Color (0, 0, 0),
353 libdcp::Time (0, 0, 0, 0),
354 libdcp::Time (0, 0, 0, 0)
357 s = subs.subtitles_at (libdcp::Time (0, 1, 27, 200));
358 BOOST_CHECK_EQUAL (s.size(), 2);
359 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
362 libdcp::Color (255, 255, 255),
364 libdcp::Time (0, 1, 27, 115),
365 libdcp::Time (0, 1, 28, 208),
368 "That curious creature the Sphinx",
370 libdcp::Color (0, 0, 0),
371 libdcp::Time (0, 0, 0, 0),
372 libdcp::Time (0, 0, 0, 0)
374 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
377 libdcp::Color (255, 255, 255),
379 libdcp::Time (0, 1, 27, 115),
380 libdcp::Time (0, 1, 28, 208),
383 "Is smarter than anyone thinks",
385 libdcp::Color (0, 0, 0),
386 libdcp::Time (0, 0, 0, 0),
387 libdcp::Time (0, 0, 0, 0)
390 s = subs.subtitles_at (libdcp::Time (0, 1, 42, 300));
391 BOOST_CHECK_EQUAL (s.size(), 2);
392 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
395 libdcp::Color (255, 255, 255),
397 libdcp::Time (0, 1, 42, 229),
398 libdcp::Time (0, 1, 45, 62),
401 "It sits there and smirks",
403 libdcp::Color (0, 0, 0),
404 libdcp::Time (0, 0, 0, 0),
405 libdcp::Time (0, 0, 0, 0)
407 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
410 libdcp::Color (255, 255, 255),
412 libdcp::Time (0, 1, 42, 229),
413 libdcp::Time (0, 1, 45, 62),
416 "And you don't think it works",
418 libdcp::Color (0, 0, 0),
419 libdcp::Time (0, 0, 0, 0),
420 libdcp::Time (0, 0, 0, 0)
423 s = subs.subtitles_at (libdcp::Time (0, 1, 45, 200));
424 BOOST_CHECK_EQUAL (s.size(), 2);
425 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
428 libdcp::Color (255, 255, 255),
430 libdcp::Time (0, 1, 45, 146),
431 libdcp::Time (0, 1, 47, 94),
434 "Then when you're not looking, it winks.",
436 libdcp::Color (0, 0, 0),
437 libdcp::Time (0, 0, 0, 0),
438 libdcp::Time (0, 0, 0, 0)
440 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
443 libdcp::Color (255, 255, 255),
445 libdcp::Time (0, 1, 45, 146),
446 libdcp::Time (0, 1, 47, 94),
449 "When it snows you will find Sister Sledge",
451 libdcp::Color (0, 0, 0),
452 libdcp::Time (0, 0, 0, 0),
453 libdcp::Time (0, 0, 0, 0)
456 s = subs.subtitles_at (libdcp::Time (0, 1, 47, 249));
457 BOOST_CHECK_EQUAL (s.size(), 2);
458 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
461 libdcp::Color (255, 255, 255),
463 libdcp::Time (0, 1, 47, 146),
464 libdcp::Time (0, 1, 48, 167),
467 "Out mooning, at night, on the ledge",
469 libdcp::Color (0, 0, 0),
470 libdcp::Time (0, 0, 0, 0),
471 libdcp::Time (0, 0, 0, 0)
473 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
476 libdcp::Color (255, 255, 255),
478 libdcp::Time (0, 1, 47, 146),
479 libdcp::Time (0, 1, 48, 167),
484 libdcp::Color (0, 0, 0),
485 libdcp::Time (0, 0, 0, 0),
486 libdcp::Time (0, 0, 0, 0)
489 s = subs.subtitles_at (libdcp::Time (0, 2, 6, 210));
490 BOOST_CHECK_EQUAL (s.size(), 2);
491 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
494 libdcp::Color (255, 255, 255),
496 libdcp::Time (0, 2, 5, 208),
497 libdcp::Time (0, 2, 7, 31),
502 libdcp::Color (0, 0, 0),
503 libdcp::Time (0, 0, 0, 0),
504 libdcp::Time (0, 0, 0, 0)
506 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
509 libdcp::Color (255, 255, 255),
511 libdcp::Time (0, 2, 5, 208),
512 libdcp::Time (0, 2, 7, 31),
517 libdcp::Color (0, 0, 0),
518 libdcp::Time (0, 0, 0, 0),
519 libdcp::Time (0, 0, 0, 0)
526 BOOST_AUTO_TEST_CASE (dcp_time)
528 libdcp::Time t (977143, 24);
530 BOOST_CHECK_EQUAL (t.t, 73);
531 BOOST_CHECK_EQUAL (t.s, 34);
532 BOOST_CHECK_EQUAL (t.m, 18);
533 BOOST_CHECK_EQUAL (t.h, 11);
534 BOOST_CHECK_EQUAL (t.to_string(), "11:18:34:73");
535 BOOST_CHECK_EQUAL (t.to_ticks(), 1017923);
537 libdcp::Time a (3, 2, 3, 4);
538 libdcp::Time b (2, 3, 4, 5);
540 libdcp::Time r = a - b;
541 BOOST_CHECK_EQUAL (r.h, 0);
542 BOOST_CHECK_EQUAL (r.m, 58);
543 BOOST_CHECK_EQUAL (r.s, 58);
544 BOOST_CHECK_EQUAL (r.t, 249);
545 BOOST_CHECK_EQUAL (r.to_string(), "0:58:58:249");
546 BOOST_CHECK_EQUAL (r.to_ticks(), 88699);
548 a = libdcp::Time (1, 58, 56, 240);
549 b = libdcp::Time (1, 7, 12, 120);
551 BOOST_CHECK_EQUAL (r.h, 3);
552 BOOST_CHECK_EQUAL (r.m, 6);
553 BOOST_CHECK_EQUAL (r.s, 9);
554 BOOST_CHECK_EQUAL (r.t, 110);
555 BOOST_CHECK_EQUAL (r.to_string(), "3:6:9:110");
556 BOOST_CHECK_EQUAL (r.to_ticks(), 279335);
558 a = libdcp::Time (24, 12, 6, 3);
559 b = libdcp::Time (16, 8, 4, 2);
560 BOOST_CHECK_CLOSE (a / b, 1.5, 1e-5);
563 BOOST_AUTO_TEST_CASE (color)
565 libdcp::Color c ("FFFF0000");
567 BOOST_CHECK_EQUAL (c.r, 255);
568 BOOST_CHECK_EQUAL (c.g, 0);
569 BOOST_CHECK_EQUAL (c.b, 0);
570 BOOST_CHECK_EQUAL (c.to_argb_string(), "FFFF0000");
572 c = libdcp::Color ("FF00FF00");
574 BOOST_CHECK_EQUAL (c.r, 0);
575 BOOST_CHECK_EQUAL (c.g, 255);
576 BOOST_CHECK_EQUAL (c.b, 0);
577 BOOST_CHECK_EQUAL (c.to_argb_string(), "FF00FF00");
579 c = libdcp::Color ("FF0000FF");
581 BOOST_CHECK_EQUAL (c.r, 0);
582 BOOST_CHECK_EQUAL (c.g, 0);
583 BOOST_CHECK_EQUAL (c.b, 255);
584 BOOST_CHECK_EQUAL (c.to_argb_string(), "FF0000FF");
588 BOOST_AUTO_TEST_CASE (recovery)
590 Kumu::libdcp_test = true;
592 string const picture = "test/data/32x32_red_square.j2c";
593 int const size = boost::filesystem::file_size (picture);
594 uint8_t* data = new uint8_t[size];
596 FILE* f = fopen (picture.c_str(), "rb");
598 fread (data, 1, size, f);
603 /* XXX: fix this posix-only stuff */
604 Kumu::ResetTestRNG ();
607 boost::filesystem::remove_all ("build/test/baz");
608 boost::filesystem::create_directories ("build/test/baz");
609 shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset ("build/test/baz", "video1.mxf", 24, libdcp::Size (32, 32)));
610 shared_ptr<libdcp::MonoPictureAssetWriter> writer = mp->start_write (false);
612 int written_size = 0;
613 for (int i = 0; i < 24; ++i) {
614 libdcp::FrameInfo info = writer->write (data, size);
615 written_size = info.size;
621 boost::filesystem::copy_file ("build/test/baz/video1.mxf", "build/test/baz/video2.mxf");
622 boost::filesystem::resize_file ("build/test/baz/video2.mxf", 16384 + 353 * 11);
625 FILE* f = fopen ("build/test/baz/video2.mxf", "r+");
628 memset (zeros, 0, 256);
629 fwrite (zeros, 1, 256, f);
634 Kumu::ResetTestRNG ();
637 mp.reset (new libdcp::MonoPictureAsset ("build/test/baz", "video2.mxf", 24, libdcp::Size (32, 32)));
638 writer = mp->start_write (true);
640 writer->write (data, size);
642 for (int i = 1; i < 4; ++i) {
643 writer->fake_write (written_size);
646 for (int i = 4; i < 24; ++i) {
647 writer->write (data, size);