Various playlist editor developments and fixes.
[dcpomatic.git] / test / reels_test.cc
index 9a915ab2b11cdee1062d50f89b038730426f6ed6..3d79017f0abdaec7be8024e313a2c6163a0aff88 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2015-2016 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2015-2019 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 #include "lib/dcp_content_type.h"
 #include "lib/dcp_content.h"
 #include "lib/video_content.h"
-#include "lib/plain_text_file_content.h"
+#include "lib/string_text_file_content.h"
 #include "lib/content_factory.h"
 #include "test.h"
 #include <boost/test/unit_test.hpp>
 #include <boost/foreach.hpp>
+#include <iostream>
 
 using std::list;
 using std::cout;
 using boost::shared_ptr;
+using namespace dcpomatic;
 
 /** Test Film::reels() */
 BOOST_AUTO_TEST_CASE (reels_test1)
 {
        shared_ptr<Film> film = new_test_film ("reels_test1");
        film->set_container (Ratio::from_id ("185"));
-       shared_ptr<FFmpegContent> A (new FFmpegContent (film, "test/data/test.mp4"));
+       shared_ptr<FFmpegContent> A (new FFmpegContent("test/data/test.mp4"));
        film->examine_and_add_content (A);
-       shared_ptr<FFmpegContent> B (new FFmpegContent (film, "test/data/test.mp4"));
+       shared_ptr<FFmpegContent> B (new FFmpegContent("test/data/test.mp4"));
        film->examine_and_add_content (B);
-       wait_for_jobs ();
-       BOOST_CHECK_EQUAL (A->full_length().get(), 288000);
+       BOOST_REQUIRE (!wait_for_jobs());
+       BOOST_CHECK_EQUAL (A->full_length(film).get(), 288000);
 
        film->set_reel_type (REELTYPE_SINGLE);
        list<DCPTimePeriod> r = film->reels ();
@@ -95,31 +97,31 @@ BOOST_AUTO_TEST_CASE (reels_test2)
        film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
 
        {
-               shared_ptr<ImageContent> c (new ImageContent (film, "test/data/flat_red.png"));
+               shared_ptr<ImageContent> c (new ImageContent("test/data/flat_red.png"));
                film->examine_and_add_content (c);
-               wait_for_jobs ();
+               BOOST_REQUIRE (!wait_for_jobs());
                c->video->set_length (24);
        }
 
        {
-               shared_ptr<ImageContent> c (new ImageContent (film, "test/data/flat_green.png"));
+               shared_ptr<ImageContent> c (new ImageContent("test/data/flat_green.png"));
                film->examine_and_add_content (c);
-               wait_for_jobs ();
+               BOOST_REQUIRE (!wait_for_jobs());
                c->video->set_length (24);
        }
 
        {
-               shared_ptr<ImageContent> c (new ImageContent (film, "test/data/flat_blue.png"));
+               shared_ptr<ImageContent> c (new ImageContent("test/data/flat_blue.png"));
                film->examine_and_add_content (c);
-               wait_for_jobs ();
+               BOOST_REQUIRE (!wait_for_jobs());
                c->video->set_length (24);
        }
 
        film->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
-       wait_for_jobs ();
+       BOOST_REQUIRE (!wait_for_jobs());
 
        film->make_dcp ();
-       wait_for_jobs ();
+       BOOST_REQUIRE (!wait_for_jobs());
 
        check_dcp ("test/data/reels_test2", film->dir (film->dcp_name()));
 
@@ -129,7 +131,7 @@ BOOST_AUTO_TEST_CASE (reels_test2)
        film2->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
        film2->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
 
-       shared_ptr<DCPContent> c (new DCPContent (film2, film->dir (film->dcp_name ())));
+       shared_ptr<DCPContent> c (new DCPContent(film->dir(film->dcp_name())));
        film2->examine_and_add_content (c);
        BOOST_REQUIRE (!wait_for_jobs ());
 
@@ -149,7 +151,7 @@ BOOST_AUTO_TEST_CASE (reels_test2)
        c->set_reference_audio (true);
 
        film2->make_dcp ();
-       wait_for_jobs ();
+       BOOST_REQUIRE (!wait_for_jobs());
 }
 
 /** Check that REELTYPE_BY_VIDEO_CONTENT adds an extra reel, if necessary, at the end
@@ -163,11 +165,11 @@ BOOST_AUTO_TEST_CASE (reels_test3)
        film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
        film->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
 
-       shared_ptr<Content> dcp (new DCPContent (film, "test/data/reels_test2"));
+       shared_ptr<Content> dcp (new DCPContent("test/data/reels_test2"));
        film->examine_and_add_content (dcp);
-       shared_ptr<Content> sub (new PlainTextFileContent (film, "test/data/subrip.srt"));
+       shared_ptr<Content> sub (new StringTextFileContent("test/data/subrip.srt"));
        film->examine_and_add_content (sub);
-       wait_for_jobs ();
+       BOOST_REQUIRE (!wait_for_jobs());
 
        list<DCPTimePeriod> reels = film->reels();
        BOOST_REQUIRE_EQUAL (reels.size(), 4);
@@ -182,7 +184,7 @@ BOOST_AUTO_TEST_CASE (reels_test3)
        BOOST_CHECK_EQUAL (i->to.get(), 96000 * 3);
        ++i;
        BOOST_CHECK_EQUAL (i->from.get(), 96000 * 3);
-       BOOST_CHECK_EQUAL (i->to.get(), sub->full_length().ceil(film->video_frame_rate()).get());
+       BOOST_CHECK_EQUAL (i->to.get(), sub->full_length(film).ceil(film->video_frame_rate()).get());
 }
 
 /** Check creation of a multi-reel DCP with a single .srt subtitle file;
@@ -200,15 +202,15 @@ BOOST_AUTO_TEST_CASE (reels_test4)
        /* 4 piece of 1s-long content */
        shared_ptr<ImageContent> content[4];
        for (int i = 0; i < 4; ++i) {
-               content[i].reset (new ImageContent (film, "test/data/flat_green.png"));
+               content[i].reset (new ImageContent("test/data/flat_green.png"));
                film->examine_and_add_content (content[i]);
-               wait_for_jobs ();
+               BOOST_REQUIRE (!wait_for_jobs());
                content[i]->video->set_length (24);
        }
 
-       shared_ptr<PlainTextFileContent> subs (new PlainTextFileContent (film, "test/data/subrip3.srt"));
+       shared_ptr<StringTextFileContent> subs (new StringTextFileContent("test/data/subrip3.srt"));
        film->examine_and_add_content (subs);
-       wait_for_jobs ();
+       BOOST_REQUIRE (!wait_for_jobs());
 
        list<DCPTimePeriod> reels = film->reels();
        BOOST_REQUIRE_EQUAL (reels.size(), 4);
@@ -226,7 +228,7 @@ BOOST_AUTO_TEST_CASE (reels_test4)
        BOOST_CHECK_EQUAL (i->to.get(), 96000 * 4);
 
        film->make_dcp ();
-       wait_for_jobs ();
+       BOOST_REQUIRE (!wait_for_jobs());
 
        check_dcp ("test/data/reels_test4", film->dir (film->dcp_name()));
 }
@@ -235,15 +237,15 @@ BOOST_AUTO_TEST_CASE (reels_test5)
 {
        shared_ptr<Film> film = new_test_film ("reels_test5");
        film->set_sequence (false);
-       shared_ptr<DCPContent> dcp (new DCPContent (film, "test/data/reels_test4"));
+       shared_ptr<DCPContent> dcp (new DCPContent("test/data/reels_test4"));
        film->examine_and_add_content (dcp);
        BOOST_REQUIRE (!wait_for_jobs ());
 
        /* Set to 2123 but it will be rounded up to the next frame (4000) */
-       dcp->set_position(DCPTime(2123));
+       dcp->set_position(film, DCPTime(2123));
 
        {
-               list<DCPTimePeriod> p = dcp->reels ();
+               list<DCPTimePeriod> p = dcp->reels (film);
                BOOST_REQUIRE_EQUAL (p.size(), 4);
                list<DCPTimePeriod>::const_iterator i = p.begin();
                BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 96000)));
@@ -254,7 +256,7 @@ BOOST_AUTO_TEST_CASE (reels_test5)
 
        {
                dcp->set_trim_start (ContentTime::from_seconds (0.5));
-               list<DCPTimePeriod> p = dcp->reels ();
+               list<DCPTimePeriod> p = dcp->reels (film);
                BOOST_REQUIRE_EQUAL (p.size(), 4);
                list<DCPTimePeriod>::const_iterator i = p.begin();
                BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 48000)));
@@ -265,7 +267,7 @@ BOOST_AUTO_TEST_CASE (reels_test5)
 
        {
                dcp->set_trim_end (ContentTime::from_seconds (0.5));
-               list<DCPTimePeriod> p = dcp->reels ();
+               list<DCPTimePeriod> p = dcp->reels (film);
                BOOST_REQUIRE_EQUAL (p.size(), 4);
                list<DCPTimePeriod>::const_iterator i = p.begin();
                BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 48000)));
@@ -276,7 +278,7 @@ BOOST_AUTO_TEST_CASE (reels_test5)
 
        {
                dcp->set_trim_start (ContentTime::from_seconds (1.5));
-               list<DCPTimePeriod> p = dcp->reels ();
+               list<DCPTimePeriod> p = dcp->reels (film);
                BOOST_REQUIRE_EQUAL (p.size(), 3);
                list<DCPTimePeriod>::const_iterator i = p.begin();
                BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 48000)));
@@ -292,7 +294,7 @@ BOOST_AUTO_TEST_CASE (reels_test6)
        film->set_name ("reels_test6");
        film->set_container (Ratio::from_id ("185"));
        film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
-       shared_ptr<FFmpegContent> A (new FFmpegContent (film, "test/data/test2.mp4"));
+       shared_ptr<FFmpegContent> A (new FFmpegContent("test/data/test2.mp4"));
        film->examine_and_add_content (A);
        BOOST_REQUIRE (!wait_for_jobs ());
 
@@ -313,10 +315,10 @@ BOOST_AUTO_TEST_CASE (reels_test7)
        film->set_name ("reels_test7");
        film->set_container (Ratio::from_id ("185"));
        film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
-       shared_ptr<Content> A = content_factory(film, "test/data/flat_red.png").front();
+       shared_ptr<Content> A = content_factory("test/data/flat_red.png").front();
        film->examine_and_add_content (A);
        BOOST_REQUIRE (!wait_for_jobs ());
-       shared_ptr<Content> B = content_factory(film, "test/data/awkward_length.wav").front();
+       shared_ptr<Content> B = content_factory("test/data/awkward_length.wav").front();
        film->examine_and_add_content (B);
        BOOST_REQUIRE (!wait_for_jobs ());
        film->set_video_frame_rate (24);
@@ -338,7 +340,7 @@ BOOST_AUTO_TEST_CASE (reels_test8)
        film->set_name ("reels_test8");
        film->set_container (Ratio::from_id ("185"));
        film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
-       shared_ptr<FFmpegContent> A (new FFmpegContent (film, "test/data/test2.mp4"));
+       shared_ptr<FFmpegContent> A (new FFmpegContent("test/data/test2.mp4"));
        film->examine_and_add_content (A);
        BOOST_REQUIRE (!wait_for_jobs ());
 
@@ -351,7 +353,7 @@ BOOST_AUTO_TEST_CASE (reels_test8)
 BOOST_AUTO_TEST_CASE (reels_test9)
 {
        shared_ptr<Film> film = new_test_film2("reels_test9a");
-       shared_ptr<FFmpegContent> A(new FFmpegContent(film, "test/data/flat_red.png"));
+       shared_ptr<FFmpegContent> A(new FFmpegContent("test/data/flat_red.png"));
        film->examine_and_add_content(A);
        BOOST_REQUIRE(!wait_for_jobs());
        A->video->set_length(5 * 24);
@@ -360,9 +362,9 @@ BOOST_AUTO_TEST_CASE (reels_test9)
        BOOST_REQUIRE(!wait_for_jobs());
 
        shared_ptr<Film> film2 = new_test_film2("reels_test9b");
-       shared_ptr<DCPContent> B(new DCPContent(film2, film->dir(film->dcp_name())));
+       shared_ptr<DCPContent> B(new DCPContent(film->dir(film->dcp_name())));
        film2->examine_and_add_content(B);
-       film2->examine_and_add_content(content_factory(film, "test/data/dcp_sub4.xml").front());
+       film2->examine_and_add_content(content_factory("test/data/dcp_sub4.xml").front());
        B->set_reference_video(true);
        B->set_reference_audio(true);
        BOOST_REQUIRE(!wait_for_jobs());
@@ -371,3 +373,112 @@ BOOST_AUTO_TEST_CASE (reels_test9)
        film2->make_dcp();
        BOOST_REQUIRE(!wait_for_jobs());
 }
+
+/** Another reels-related error; make_dcp() would raise a ProgrammingError
+ *  in AudioBuffers::allocate due to an attempt to allocate a negatively-sized buffer.
+ *  This was triggered by a VF where there are referenced audio reels followed by
+ *  VF audio.  When the VF audio arrives the Writer did not correctly skip over the
+ *  referenced reels.
+ */
+BOOST_AUTO_TEST_CASE (reels_test10)
+{
+       /* Make the OV */
+       shared_ptr<Film> ov = new_test_film2("reels_test10_ov");
+       shared_ptr<FFmpegContent> A(new FFmpegContent("test/data/flat_red.png"));
+       ov->examine_and_add_content (A);
+       BOOST_REQUIRE (!wait_for_jobs());
+       A->video->set_length (5 * 24);
+
+       shared_ptr<FFmpegContent> B(new FFmpegContent("test/data/flat_red.png"));
+       ov->examine_and_add_content (B);
+       BOOST_REQUIRE (!wait_for_jobs());
+       B->video->set_length (5 * 24);
+
+       ov->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
+       ov->make_dcp ();
+       BOOST_REQUIRE (!wait_for_jobs());
+       ov->write_metadata ();
+
+       /* Now try to make the VF; this used to fail */
+       shared_ptr<Film> vf = new_test_film2("reels_test10_vf");
+       shared_ptr<DCPContent> ov_dcp(new DCPContent(ov->dir(ov->dcp_name())));
+       vf->examine_and_add_content (ov_dcp);
+       BOOST_REQUIRE (!wait_for_jobs());
+       vf->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
+       ov_dcp->set_reference_video (true);
+       ov_dcp->set_reference_audio (true);
+       vf->examine_and_add_content (content_factory("test/data/15s.srt").front());
+       BOOST_REQUIRE (!wait_for_jobs());
+
+       vf->make_dcp ();
+       BOOST_REQUIRE (!wait_for_jobs());
+       vf->write_metadata ();
+}
+
+/** Another reels error; REELTYPE_BY_VIDEO_CONTENT when the first content is not
+ *  at time 0.
+ */
+BOOST_AUTO_TEST_CASE (reels_test11)
+{
+       shared_ptr<Film> film = new_test_film2 ("reels_test11");
+       film->set_video_frame_rate (24);
+       shared_ptr<FFmpegContent> A(new FFmpegContent("test/data/flat_red.png"));
+       film->examine_and_add_content (A);
+       BOOST_REQUIRE (!wait_for_jobs());
+       A->video->set_length (240);
+       A->set_video_frame_rate (24);
+       A->set_position (film, DCPTime::from_seconds(1));
+       film->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
+       film->make_dcp ();
+       BOOST_REQUIRE (!wait_for_jobs());
+       BOOST_CHECK_EQUAL (A->position().get(), DCPTime::from_seconds(1).get());
+       BOOST_CHECK_EQUAL (A->end(film).get(), DCPTime::from_seconds(1 + 10).get());
+
+       list<DCPTimePeriod> r = film->reels ();
+       BOOST_CHECK_EQUAL (r.size(), 2);
+       BOOST_CHECK_EQUAL (r.front().from.get(), 0);
+       BOOST_CHECK_EQUAL (r.front().to.get(), DCPTime::from_seconds(1).get());
+       BOOST_CHECK_EQUAL (r.back().from.get(), DCPTime::from_seconds(1).get());
+       BOOST_CHECK_EQUAL (r.back().to.get(), DCPTime::from_seconds(1 + 10).get());
+}
+
+/** For VFs to work right we have to make separate reels for empty bits between
+ *  video content.
+ */
+BOOST_AUTO_TEST_CASE (reels_test12)
+{
+       shared_ptr<Film> film = new_test_film2 ("reels_test12");
+       film->set_video_frame_rate (24);
+       film->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
+       film->set_sequence (false);
+
+       shared_ptr<FFmpegContent> A(new FFmpegContent("test/data/flat_red.png"));
+       film->examine_and_add_content (A);
+       BOOST_REQUIRE (!wait_for_jobs());
+       A->video->set_length (240);
+       A->set_video_frame_rate (24);
+       A->set_position (film, DCPTime::from_seconds(1));
+
+       shared_ptr<FFmpegContent> B(new FFmpegContent("test/data/flat_red.png"));
+       film->examine_and_add_content (B);
+       BOOST_REQUIRE (!wait_for_jobs());
+       B->video->set_length (120);
+       B->set_video_frame_rate (24);
+       B->set_position (film, DCPTime::from_seconds(14));
+
+       list<DCPTimePeriod> r = film->reels ();
+       BOOST_REQUIRE_EQUAL (r.size(), 4);
+       list<DCPTimePeriod>::const_iterator i = r.begin ();
+
+       BOOST_CHECK_EQUAL (i->from.get(), 0);
+       BOOST_CHECK_EQUAL (i->to.get(),   DCPTime::from_seconds(1).get());
+       ++i;
+       BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_seconds(1).get());
+       BOOST_CHECK_EQUAL (i->to.get(),   DCPTime::from_seconds(11).get());
+       ++i;
+       BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_seconds(11).get());
+       BOOST_CHECK_EQUAL (i->to.get(),   DCPTime::from_seconds(14).get());
+       ++i;
+       BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_seconds(14).get());
+       BOOST_CHECK_EQUAL (i->to.get(),   DCPTime::from_seconds(19).get());
+}