Bv2.1 7.1: Check picture size and frame rate.
authorCarl Hetherington <cth@carlh.net>
Sun, 13 Dec 2020 20:23:41 +0000 (21:23 +0100)
committerCarl Hetherington <cth@carlh.net>
Sun, 17 Jan 2021 19:13:22 +0000 (20:13 +0100)
BRANCH
src/verify.cc
src/verify.h
test/verify_test.cc

diff --git a/BRANCH b/BRANCH
index 9ea3ee097f6d8e496412d6c585b8ed5b5a3cad53..11a7d47bf9cc5cc702464e73c9686c930b859ab7 100644 (file)
--- a/BRANCH
+++ b/BRANCH
@@ -17,5 +17,5 @@ Mark things with [Bv2.1_paragraph]
     - CPL metadata ReleaseTerritory
 
 7 [/]
-7.1 picture essence encoding; frame size, rate and 2D/3D will be one of the four allowed combinations
+7.1 picture essence encoding; frame size, rate and 2D/3D will be one of the four allowed combinations [/]
 
index d78e6462b693fff2848003f97bdc6369ec89e1a8..78c087da86ff79030c1fe5959afc935441ab0d20 100644 (file)
@@ -480,7 +480,8 @@ verify_main_picture_asset (
        list<VerificationNote>& notes
        )
 {
-       boost::filesystem::path const file = *reel_asset->asset()->file();
+       shared_ptr<const PictureAsset> asset = reel_asset->asset();
+       boost::filesystem::path const file = *asset->file();
        stage ("Checking picture asset hash", file);
        VerifyAssetResult const r = verify_asset (dcp, reel_asset, progress);
        switch (r) {
@@ -501,7 +502,7 @@ verify_main_picture_asset (
                default:
                        break;
        }
-       stage ("Checking picture frame sizes", reel_asset->asset()->file());
+       stage ("Checking picture frame sizes", asset->file());
        VerifyPictureAssetResult const pr = verify_picture_asset (reel_asset, progress);
        switch (pr) {
                case VERIFY_PICTURE_ASSET_RESULT_BAD:
@@ -521,6 +522,64 @@ verify_main_picture_asset (
                default:
                        break;
        }
+
+       /* Only flat/scope allowed by Bv2.1 */
+       if (
+               asset->size() != dcp::Size(2048, 858) &&
+               asset->size() != dcp::Size(1998, 1080) &&
+               asset->size() != dcp::Size(4096, 1716) &&
+               asset->size() != dcp::Size(3996, 2160)) {
+               notes.push_back(
+                       VerificationNote(
+                               VerificationNote::VERIFY_BV21_ERROR,
+                               VerificationNote::PICTURE_ASSET_INVALID_SIZE_IN_PIXELS,
+                               String::compose("%1x%2", asset->size().width, asset->size().height),
+                               file
+                               )
+                       );
+       }
+
+       /* Only 24, 25, 48fps allowed for 2K */
+       if (
+               (asset->size() == dcp::Size(2048, 858) || asset->size() == dcp::Size(1998, 1080)) &&
+               (asset->edit_rate() != dcp::Fraction(24, 1) && asset->edit_rate() != dcp::Fraction(25, 1) && asset->edit_rate() != dcp::Fraction(48, 1))
+          ) {
+               notes.push_back(
+                       VerificationNote(
+                               VerificationNote::VERIFY_BV21_ERROR,
+                               VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K,
+                               String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator),
+                               file
+                               )
+                       );
+       }
+
+       if (asset->size() == dcp::Size(4096, 1716) || asset->size() == dcp::Size(3996, 2160)) {
+               /* Only 24fps allowed for 4K */
+               if (asset->edit_rate() != dcp::Fraction(24, 1)) {
+                       notes.push_back(
+                               VerificationNote(
+                                       VerificationNote::VERIFY_BV21_ERROR,
+                                       VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K,
+                                       String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator),
+                                       file
+                                       )
+                               );
+               }
+
+               /* Only 2D allowed for 4K */
+               if (dynamic_pointer_cast<const StereoPictureAsset>(asset)) {
+                       notes.push_back(
+                               VerificationNote(
+                                       VerificationNote::VERIFY_BV21_ERROR,
+                                       VerificationNote::PICTURE_ASSET_4K_3D,
+                                       file
+                                       )
+                               );
+
+               }
+       }
+
 }
 
 
@@ -768,6 +827,15 @@ dcp::note_to_string (dcp::VerificationNote note)
                return "This DCP does not use the SMPTE standard, which is required for Bv2.1 compliance.";
        case dcp::VerificationNote::BAD_LANGUAGE:
                return String::compose("The DCP specifies a language '%1' which does not conform to the RFC 5646 standard.", note.note().get());
+       case dcp::VerificationNote::PICTURE_ASSET_INVALID_SIZE_IN_PIXELS:
+               return String::compose("A picture asset's size (%1) is not one of those allowed by Bv2.1 (2048x858, 1998x1080, 4096x1716 or 3996x2160)", note.note().get());
+       case dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K:
+               return String::compose("A picture asset's frame rate (%1) is not one of those allowed for 2K DCPs by Bv2.1 (24, 25 or 48fps)", note.note().get());
+       case dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K:
+               return String::compose("A picture asset's frame rate (%1) is not 24fps as required for 4K DCPs by Bv2.1", note.note().get());
+       case dcp::VerificationNote::PICTURE_ASSET_4K_3D:
+               return "3D 4K DCPs are not allowed by Bv2.1";
+
        }
 
        return "";
index 24a9e671e3ea390763bad8d05324ef925849d938..500918deb1fb43e744de78fb08b09a6531e19942 100644 (file)
@@ -94,6 +94,14 @@ public:
                NOT_SMPTE,
                /** A language or territory does not conform to RFC 5646 [Bv2.1_6.2.1] */
                BAD_LANGUAGE,
+               /** A picture asset does not have one of the required Bv2.1 sizes (in pixels) [Bv2.1_7.1] */
+               PICTURE_ASSET_INVALID_SIZE_IN_PIXELS,
+               /** A picture asset is 2K but is not at 24, 25 or 48 fps as required by Bv2.1 [Bv2.1_7.1] */
+               PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K,
+               /** A picture asset is 4K but is not at 24fps as required by Bv2.1 [Bv2.1_7.1] */
+               PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K,
+               /** A picture asset is 4K but is 3D which is not allowed by Bv2.1 [Bv2.1_7.1] */
+               PICTURE_ASSET_4K_3D,
        };
 
        VerificationNote (Type type, Code code)
index e74a737a8be5d727c0ffe040ca6ab5265e780911..e8396dd1a389f6532c823f31a16d574c2d07ee02 100644 (file)
 #include "dcp.h"
 #include "openjpeg_image.h"
 #include "mono_picture_asset.h"
+#include "stereo_picture_asset.h"
 #include "mono_picture_asset_writer.h"
 #include "interop_subtitle_asset.h"
 #include "smpte_subtitle_asset.h"
 #include "reel_closed_caption_asset.h"
+#include "reel_stereo_picture_asset.h"
 #include "reel_subtitle_asset.h"
 #include "compose.hpp"
 #include "test.h"
@@ -1002,3 +1004,156 @@ BOOST_AUTO_TEST_CASE (verify_various_invalid_languages)
        ++i;
 }
 
+
+static
+list<dcp::VerificationNote>
+check_picture_size (int width, int height, int frame_rate, bool three_d)
+{
+       using namespace boost::filesystem;
+
+       path dcp_path = "build/test/verify_picture_test";
+       remove_all (dcp_path);
+       create_directories (dcp_path);
+
+       shared_ptr<dcp::PictureAsset> mp;
+       if (three_d) {
+               mp.reset (new dcp::StereoPictureAsset(dcp::Fraction(frame_rate, 1), dcp::SMPTE));
+       } else {
+               mp.reset (new dcp::MonoPictureAsset(dcp::Fraction(frame_rate, 1), dcp::SMPTE));
+       }
+       shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write (dcp_path / "video.mxf", false);
+
+       shared_ptr<dcp::OpenJPEGImage> image = black_image (dcp::Size(width, height));
+       dcp::ArrayData j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
+       int const length = three_d ? frame_rate * 2 : frame_rate;
+       for (int i = 0; i < length; ++i) {
+               picture_writer->write (j2c.data(), j2c.size());
+       }
+       picture_writer->finalize ();
+
+       shared_ptr<dcp::DCP> d (new dcp::DCP(dcp_path));
+       shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::FEATURE));
+       cpl->set_annotation_text ("A Test DCP");
+       cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
+
+       shared_ptr<dcp::Reel> reel(new dcp::Reel());
+
+       if (three_d) {
+               reel->add (
+                       shared_ptr<dcp::ReelPictureAsset>(
+                               new dcp::ReelStereoPictureAsset(
+                                       std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp),
+                                       0)
+                               )
+                       );
+       } else {
+               reel->add (
+                       shared_ptr<dcp::ReelPictureAsset>(
+                               new dcp::ReelMonoPictureAsset(
+                                       std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp),
+                                       0)
+                               )
+                       );
+       }
+
+       cpl->add (reel);
+
+       d->add (cpl);
+       d->write_xml (dcp::SMPTE);
+
+       vector<boost::filesystem::path> dirs;
+       dirs.push_back (dcp_path);
+       return dcp::verify (dirs, &stage, &progress, xsd_test);
+}
+
+
+static
+void
+check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
+{
+       list<dcp::VerificationNote> notes = check_picture_size(width, height, frame_rate, three_d);
+       dump_notes (notes);
+       BOOST_CHECK_EQUAL (notes.size(), 0U);
+}
+
+
+static
+void
+check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
+{
+       list<dcp::VerificationNote> notes = check_picture_size(width, height, frame_rate, three_d);
+       BOOST_REQUIRE_EQUAL (notes.size(), 1U);
+       BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
+       BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_SIZE_IN_PIXELS);
+}
+
+
+static
+void
+check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
+{
+       list<dcp::VerificationNote> notes = check_picture_size(width, height, frame_rate, three_d);
+       BOOST_REQUIRE_EQUAL (notes.size(), 2U);
+       BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
+       BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K);
+}
+
+
+static
+void
+check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
+{
+       list<dcp::VerificationNote> notes = check_picture_size(width, height, frame_rate, three_d);
+       BOOST_REQUIRE_EQUAL (notes.size(), 1U);
+       BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
+       BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K);
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_picture_size)
+{
+       using namespace boost::filesystem;
+
+       /* 2K scope */
+       check_picture_size_ok (2048, 858, 24, false);
+       check_picture_size_ok (2048, 858, 25, false);
+       check_picture_size_ok (2048, 858, 48, false);
+       check_picture_size_ok (2048, 858, 24, true);
+       check_picture_size_ok (2048, 858, 25, true);
+       check_picture_size_ok (2048, 858, 48, true);
+
+       /* 2K flat */
+       check_picture_size_ok (1998, 1080, 24, false);
+       check_picture_size_ok (1998, 1080, 25, false);
+       check_picture_size_ok (1998, 1080, 48, false);
+       check_picture_size_ok (1998, 1080, 24, true);
+       check_picture_size_ok (1998, 1080, 25, true);
+       check_picture_size_ok (1998, 1080, 48, true);
+
+       /* 4K scope */
+       check_picture_size_ok (4096, 1716, 24, false);
+
+       /* 4K flat */
+       check_picture_size_ok (3996, 2160, 24, false);
+
+       /* Bad frame size */
+       check_picture_size_bad_frame_size (2050, 858, 24, false);
+       check_picture_size_bad_frame_size (2048, 658, 25, false);
+       check_picture_size_bad_frame_size (1920, 1080, 48, true);
+       check_picture_size_bad_frame_size (4000, 3000, 24, true);
+
+       /* Bad 2K frame rate */
+       check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
+       check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
+       check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
+
+       /* Bad 4K frame rate */
+       check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
+       check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
+
+       /* No 4K 3D */
+       list<dcp::VerificationNote> notes = check_picture_size(3996, 2160, 24, true);
+       BOOST_REQUIRE_EQUAL (notes.size(), 1U);
+       BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
+       BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_4K_3D);
+}