Clamp results correctly when shifting video levels, and account for that in tests.
[dcpomatic.git] / test / video_level_test.cc
index 9514e29a0f977b07aab3c42fc43e6cc6058698f0..c974c938d3002f71a4fc86b9f1518ab8597bbd9b 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -28,6 +28,7 @@
 #include "lib/content_factory.h"
 #include "lib/content_video.h"
 #include "lib/dcp_content.h"
+#include "lib/decoder_factory.h"
 #include "lib/film.h"
 #include "lib/ffmpeg_content.h"
 #include "lib/ffmpeg_decoder.h"
@@ -125,17 +126,6 @@ BOOST_AUTO_TEST_CASE (ffmpeg_image_video_range_expanded)
 }
 
 
-static optional<ContentVideo> content_video;
-
-
-static
-void
-video_handler (ContentVideo cv)
-{
-       content_video = cv;
-}
-
-
 static
 pair<int, int>
 pixel_range (shared_ptr<const Image> image)
@@ -197,26 +187,13 @@ pixel_range (shared_ptr<const Image> image)
 
 static
 pair<int, int>
-pixel_range (shared_ptr<Film> film, shared_ptr<const FFmpegContent> content)
-{
-       shared_ptr<FFmpegDecoder> decoder(new FFmpegDecoder(film, content, false));
-       decoder->video->Data.connect (bind(&video_handler, _1));
-       content_video = boost::none;
-       while (!content_video) {
-               BOOST_REQUIRE (!decoder->pass());
-       }
-
-       return pixel_range (content_video->image->image().image);
-}
-
-
-static
-pair<int, int>
-pixel_range (shared_ptr<Film> film, shared_ptr<const ImageContent> content)
+pixel_range (shared_ptr<const Film> film, shared_ptr<const Content> content)
 {
-       shared_ptr<ImageDecoder> decoder(new ImageDecoder(film, content));
-       decoder->video->Data.connect (bind(&video_handler, _1));
-       content_video = boost::none;
+       auto decoder = decoder_factory(film, content, false, false, shared_ptr<Decoder>());
+       optional<ContentVideo> content_video;
+       decoder->video->Data.connect ([&content_video](ContentVideo cv) {
+               content_video = cv;
+       });
        while (!content_video) {
                BOOST_REQUIRE (!decoder->pass());
        }
@@ -232,9 +209,9 @@ pixel_range (boost::filesystem::path dcp_path)
        dcp::DCP dcp (dcp_path);
        dcp.read ();
 
-       shared_ptr<dcp::MonoPictureAsset> picture = dynamic_pointer_cast<dcp::MonoPictureAsset>(dcp.cpls().front()->reels().front()->main_picture()->asset());
+       auto picture = dynamic_pointer_cast<dcp::MonoPictureAsset>(dcp.cpls().front()->reels().front()->main_picture()->asset());
        BOOST_REQUIRE (picture);
-       shared_ptr<dcp::OpenJPEGImage> frame = picture->start_read()->get_frame(0)->xyz_image();
+       auto frame = picture->start_read()->get_frame(0)->xyz_image();
 
        int const width = frame->size().width;
        int const height = frame->size().height;
@@ -267,13 +244,13 @@ static
 shared_ptr<Film>
 movie_V (string name)
 {
-       shared_ptr<Film> film = new_test_film2 (name);
-       shared_ptr<FFmpegContent> content = dynamic_pointer_cast<FFmpegContent>(content_factory("test/data/rgb_grey_testcard.mp4").front());
+       auto film = new_test_film2 (name);
+       auto content = dynamic_pointer_cast<FFmpegContent>(content_factory("test/data/rgb_grey_testcard.mp4").front());
        BOOST_REQUIRE (content);
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs());
 
-       pair<int, int> range = pixel_range (film, content);
+       auto range = pixel_range (film, content);
        BOOST_CHECK_EQUAL (range.first, 15);
        BOOST_CHECK_EQUAL (range.second, 243);
 
@@ -285,14 +262,14 @@ static
 shared_ptr<Film>
 movie_VoF (string name)
 {
-       shared_ptr<Film> film = new_test_film2 (name);
-       shared_ptr<FFmpegContent> content = dynamic_pointer_cast<FFmpegContent>(content_factory("test/data/rgb_grey_testcard.mp4").front());
+       auto film = new_test_film2 (name);
+       auto content = dynamic_pointer_cast<FFmpegContent>(content_factory("test/data/rgb_grey_testcard.mp4").front());
        BOOST_REQUIRE (content);
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs());
        content->video->set_range (VideoRange::FULL);
 
-       pair<int, int> range = pixel_range (film, content);
+       auto range = pixel_range (film, content);
        BOOST_CHECK_EQUAL (range.first, 15);
        BOOST_CHECK_EQUAL (range.second, 243);
 
@@ -304,13 +281,13 @@ static
 shared_ptr<Film>
 movie_F (string name)
 {
-       shared_ptr<Film> film = new_test_film2 (name);
-       shared_ptr<FFmpegContent> content = dynamic_pointer_cast<FFmpegContent>(content_factory("test/data/rgb_grey_testcard.mov").front());
+       auto film = new_test_film2 (name);
+       auto content = dynamic_pointer_cast<FFmpegContent>(content_factory("test/data/rgb_grey_testcard.mov").front());
        BOOST_REQUIRE (content);
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs());
 
-       pair<int, int> range = pixel_range (film, content);
+       auto range = pixel_range (film, content);
        BOOST_CHECK_EQUAL (range.first, 0);
        BOOST_CHECK_EQUAL (range.second, 1023);
 
@@ -322,14 +299,14 @@ static
 shared_ptr<Film>
 movie_FoV (string name)
 {
-       shared_ptr<Film> film = new_test_film2 (name);
-       shared_ptr<FFmpegContent> content = dynamic_pointer_cast<FFmpegContent>(content_factory("test/data/rgb_grey_testcard.mov").front());
+       auto film = new_test_film2 (name);
+       auto content = dynamic_pointer_cast<FFmpegContent>(content_factory("test/data/rgb_grey_testcard.mov").front());
        BOOST_REQUIRE (content);
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs());
        content->video->set_range (VideoRange::VIDEO);
 
-       pair<int, int> range = pixel_range (film, content);
+       auto range = pixel_range (film, content);
        BOOST_CHECK_EQUAL (range.first, 0);
        BOOST_CHECK_EQUAL (range.second, 1023);
 
@@ -341,13 +318,13 @@ static
 shared_ptr<Film>
 image_F (string name)
 {
-       shared_ptr<Film> film = new_test_film2 (name);
-       shared_ptr<ImageContent> content = dynamic_pointer_cast<ImageContent>(content_factory("test/data/rgb_grey_testcard.png").front());
+       auto film = new_test_film2 (name);
+       auto content = dynamic_pointer_cast<ImageContent>(content_factory("test/data/rgb_grey_testcard.png").front());
        BOOST_REQUIRE (content);
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs());
 
-       pair<int, int> range = pixel_range (film, content);
+       auto range = pixel_range (film, content);
        BOOST_CHECK_EQUAL (range.first, 0);
        BOOST_CHECK_EQUAL (range.second, 255);
 
@@ -359,16 +336,19 @@ static
 shared_ptr<Film>
 image_FoV (string name)
 {
-       shared_ptr<Film> film = new_test_film2 (name);
-       shared_ptr<ImageContent> content = dynamic_pointer_cast<ImageContent>(content_factory("test/data/rgb_grey_testcard.png").front());
+       auto film = new_test_film2 (name);
+       auto content = dynamic_pointer_cast<ImageContent>(content_factory("test/data/rgb_grey_testcard.png").front());
        BOOST_REQUIRE (content);
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs());
        content->video->set_range (VideoRange::VIDEO);
 
-       pair<int, int> range = pixel_range (film, content);
-       BOOST_CHECK_EQUAL (range.first, 11);
-       BOOST_CHECK_EQUAL (range.second, 250);
+       auto range = pixel_range (film, content);
+       /* We are taking some full-range content and saying it should be read as video range, after which its
+        * pixels will still be full range.
+        */
+       BOOST_CHECK_EQUAL (range.first, 0);
+       BOOST_CHECK_EQUAL (range.second, 255);
 
        return film;
 }
@@ -379,12 +359,12 @@ shared_ptr<Film>
 dcp_F (string name)
 {
        boost::filesystem::path const dcp = "test/data/RgbGreyTestcar_TST-1_F_MOS_2K_20201115_SMPTE_OV";
-       shared_ptr<Film> film = new_test_film2 (name);
-       shared_ptr<DCPContent> content(new DCPContent(dcp));
-       film->examine_and_add_content (shared_ptr<DCPContent>(new DCPContent(dcp)));
+       auto film = new_test_film2 (name);
+       auto content = make_shared<DCPContent>(dcp);
+       film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs());
 
-       pair<int, int> range = pixel_range (dcp);
+       auto range = pixel_range (dcp);
        BOOST_CHECK_EQUAL (range.first, 0);
        BOOST_CHECK_EQUAL (range.second, 4081);
 
@@ -401,8 +381,7 @@ static
 pair<int, int>
 dcp_range (shared_ptr<Film> film)
 {
-       film->make_dcp ();
-       BOOST_REQUIRE (!wait_for_jobs());
+       make_and_verify_dcp (film);
        return pixel_range (film->dir(film->dcp_name()));
 }
 
@@ -412,11 +391,9 @@ static
 pair<int, int>
 V_movie_range (shared_ptr<Film> film)
 {
-       shared_ptr<TranscodeJob> job (new TranscodeJob(film));
+       auto job = make_shared<TranscodeJob>(film);
        job->set_encoder (
-               shared_ptr<FFmpegEncoder>(
-                       new FFmpegEncoder (film, job, film->file("export.mov"), ExportFormat::PRORES, true, false, false, 23)
-                       )
+               make_shared<FFmpegEncoder>(film, job, film->file("export.mov"), ExportFormat::PRORES, true, false, false, 23)
                );
        JobManager::instance()->add (job);
        BOOST_REQUIRE (!wait_for_jobs());
@@ -435,61 +412,58 @@ V_movie_range (shared_ptr<Film> film)
 
 BOOST_AUTO_TEST_CASE (movie_V_to_dcp)
 {
-       pair<int, int> range = dcp_range (movie_V("movie_V_to_dcp"));
+       auto range = dcp_range (movie_V("movie_V_to_dcp"));
        /* Video range has been correctly expanded to full for the DCP */
-       BOOST_CHECK_EQUAL (range.first, 0);
-       BOOST_CHECK_EQUAL (range.second, 4083);
+       check_int_close (range, {0, 4083}, 2);
 }
 
 
 BOOST_AUTO_TEST_CASE (movie_VoF_to_dcp)
 {
-       pair<int, int> range = dcp_range (movie_VoF("movie_VoF_to_dcp"));
+       auto range = dcp_range (movie_VoF("movie_VoF_to_dcp"));
        /* We said that video range data was really full range, so here we are in the DCP
         * with video-range data.
         */
-       BOOST_CHECK_EQUAL (range.first, 350);
-       BOOST_CHECK_EQUAL (range.second, 3832);
+       check_int_close (range, {350, 3832}, 2);
 }
 
 
 BOOST_AUTO_TEST_CASE (movie_F_to_dcp)
 {
-       pair<int, int> range = dcp_range (movie_F("movie_F_to_dcp"));
+       auto range = dcp_range (movie_F("movie_F_to_dcp"));
        /* The nearly-full-range of the input has been preserved */
-       BOOST_CHECK_EQUAL (range.first, 0);
-       BOOST_CHECK_EQUAL (range.second, 4083);
+       check_int_close (range, {0, 4083}, 2);
 }
 
 
 BOOST_AUTO_TEST_CASE (video_FoV_to_dcp)
 {
-       pair<int, int> range = dcp_range (movie_FoV("video_FoV_to_dcp"));
+       auto range = dcp_range (movie_FoV("video_FoV_to_dcp"));
        /* The nearly-full-range of the input has become even more full, and clipped */
-       BOOST_CHECK_EQUAL (range.first, 0);
-       BOOST_CHECK_EQUAL (range.second, 4095);
+       check_int_close (range, {0, 4095}, 2);
 }
 
 
 BOOST_AUTO_TEST_CASE (image_F_to_dcp)
 {
-       pair<int, int> range = dcp_range (image_F("image_F_to_dcp"));
-       BOOST_CHECK_EQUAL (range.first, 0);
-       BOOST_CHECK_EQUAL (range.second, 4083);
+       auto range = dcp_range (image_F("image_F_to_dcp"));
+       check_int_close (range, {0, 4083}, 3);
 }
 
 
 BOOST_AUTO_TEST_CASE (image_FoV_to_dcp)
 {
-       pair<int, int> range = dcp_range (image_FoV("image_FoV_to_dcp"));
-       BOOST_CHECK_EQUAL (range.first, 430);
-       BOOST_CHECK_EQUAL (range.second, 4012);
+       auto range = dcp_range (image_FoV("image_FoV_to_dcp"));
+       /* The nearly-full-range of the input has become even more full, and clipped.
+        * XXX: I'm not sure why this doesn't quite hit 4095.
+        */
+       check_int_close (range, {0, 4095}, 16);
 }
 
 
 BOOST_AUTO_TEST_CASE (movie_V_to_V_movie)
 {
-       pair<int, int> range = V_movie_range (movie_V("movie_V_to_V_movie"));
+       auto range = V_movie_range (movie_V("movie_V_to_V_movie"));
        BOOST_CHECK_EQUAL (range.first, 60);
        BOOST_CHECK_EQUAL (range.second, 998);
 }
@@ -497,7 +471,7 @@ BOOST_AUTO_TEST_CASE (movie_V_to_V_movie)
 
 BOOST_AUTO_TEST_CASE (movie_VoF_to_V_movie)
 {
-       pair<int, int> range = V_movie_range (movie_VoF("movie_VoF_to_V_movie"));
+       auto range = V_movie_range (movie_VoF("movie_VoF_to_V_movie"));
        BOOST_CHECK_EQUAL (range.first, 116);
        BOOST_CHECK_EQUAL (range.second, 939);
 }
@@ -505,7 +479,7 @@ BOOST_AUTO_TEST_CASE (movie_VoF_to_V_movie)
 
 BOOST_AUTO_TEST_CASE (movie_F_to_V_movie)
 {
-       pair<int, int> range = V_movie_range (movie_F("movie_F_to_V_movie"));
+       auto range = V_movie_range (movie_F("movie_F_to_V_movie"));
        BOOST_CHECK_EQUAL (range.first, 4);
        BOOST_CHECK_EQUAL (range.second, 1019);
 }
@@ -513,7 +487,7 @@ BOOST_AUTO_TEST_CASE (movie_F_to_V_movie)
 
 BOOST_AUTO_TEST_CASE (movie_FoV_to_V_movie)
 {
-       pair<int, int> range = V_movie_range (movie_FoV("movie_FoV_to_V_movie"));
+       auto range = V_movie_range (movie_FoV("movie_FoV_to_V_movie"));
        BOOST_CHECK_EQUAL (range.first, 4);
        BOOST_CHECK_EQUAL (range.second, 1019);
 }
@@ -521,7 +495,7 @@ BOOST_AUTO_TEST_CASE (movie_FoV_to_V_movie)
 
 BOOST_AUTO_TEST_CASE (image_F_to_V_movie)
 {
-       pair<int, int> range = V_movie_range (image_F("image_F_to_V_movie"));
+       auto range = V_movie_range (image_F("image_F_to_V_movie"));
        BOOST_CHECK_EQUAL (range.first, 64);
        BOOST_CHECK_EQUAL (range.second, 960);
 }
@@ -529,7 +503,7 @@ BOOST_AUTO_TEST_CASE (image_F_to_V_movie)
 
 BOOST_AUTO_TEST_CASE (image_FoV_to_V_movie)
 {
-       pair<int, int> range = V_movie_range (image_FoV("image_FoV_to_V_movie"));
+       auto range = V_movie_range (image_FoV("image_FoV_to_V_movie"));
        BOOST_CHECK_EQUAL (range.first, 102);
        BOOST_CHECK_EQUAL (range.second, 923);
 }
@@ -537,7 +511,7 @@ BOOST_AUTO_TEST_CASE (image_FoV_to_V_movie)
 
 BOOST_AUTO_TEST_CASE (dcp_F_to_V_movie)
 {
-       pair<int, int> range = V_movie_range (dcp_F("dcp_F_to_V_movie"));
+       auto range = V_movie_range (dcp_F("dcp_F_to_V_movie"));
        BOOST_CHECK_EQUAL (range.first, 64);
        BOOST_CHECK_EQUAL (range.second, 944);
 }