From acfbd1c01020ea95d9e7eb3d63ddb14b9407732a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 1 Feb 2021 01:24:55 +0100 Subject: [PATCH] Add noise to very small J2K frames (#1902). --- src/lib/dcp_video.cc | 40 ++++++++++++++++++++++++++++------ test/low_bitrate_test.cc | 47 ++++++++++++++++++++++++++++++++++++++++ test/wscript | 1 + 3 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 test/low_bitrate_test.cc diff --git a/src/lib/dcp_video.cc b/src/lib/dcp_video.cc index 58e10f0ed..cb1517727 100644 --- a/src/lib/dcp_video.cc +++ b/src/lib/dcp_video.cc @@ -124,15 +124,41 @@ DCPVideo::encode_locally () { auto const comment = Config::instance()->dcp_j2k_comment(); - auto enc = dcp::compress_j2k ( - convert_to_xyz (_frame, boost::bind(&Log::dcp_log, dcpomatic_log.get(), _1, _2)), - _j2k_bandwidth, - _frames_per_second, - _frame->eyes() == Eyes::LEFT || _frame->eyes() == Eyes::RIGHT, - _resolution == Resolution::FOUR_K, - comment.empty() ? "libdcp" : comment + ArrayData enc = {}; + int constexpr minimum_size = 65536; + + auto xyz = convert_to_xyz (_frame, boost::bind(&Log::dcp_log, dcpomatic_log.get(), _1, _2)); + while (true) { + enc = dcp::compress_j2k ( + xyz, + _j2k_bandwidth, + _frames_per_second, + _frame->eyes() == Eyes::LEFT || _frame->eyes() == Eyes::RIGHT, + _resolution == Resolution::FOUR_K, + comment.empty() ? "libdcp" : comment ); + if (enc.size() >= minimum_size) { + break; + } + + /* The JPEG2000 is too low-bitrate for some decoders DSS200 so add some noise + * and try again. This is slow but hopefully won't happen too often. We have to do + * convert_to_xyz() again because compress_j2k() corrupts its xyz parameter. + */ + + xyz = convert_to_xyz (_frame, boost::bind(&Log::dcp_log, dcpomatic_log.get(), _1, _2)); + auto size = xyz->size (); + auto pixels = size.width * size.height; + for (auto c = 0; c < 3; ++c) { + auto p = xyz->data(c); + for (auto i = 0; i < pixels; ++i) { + *p = std::min(4095, std::max(0, *p + rand() % 2)); + ++p; + } + } + } + switch (_frame->eyes()) { case Eyes::BOTH: LOG_DEBUG_ENCODE (N_("Finished locally-encoded frame %1 for mono"), _index); diff --git a/test/low_bitrate_test.cc b/test/low_bitrate_test.cc new file mode 100644 index 000000000..f7edecb62 --- /dev/null +++ b/test/low_bitrate_test.cc @@ -0,0 +1,47 @@ +/* + Copyright (C) 2021 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + + +#include "lib/dcp_video.h" +#include "lib/image.h" +#include "lib/player_video.h" +#include "lib/raw_image_proxy.h" +extern "C" { +#include +} +#include +#include + + +using std::make_shared; + + +BOOST_AUTO_TEST_CASE (low_bitrate_test) +{ + auto image = make_shared(AV_PIX_FMT_RGB24, dcp::Size(1998, 1080), true); + image->make_black (); + auto proxy = make_shared(image); + auto frame = make_shared(proxy, Crop(), boost::optional(), dcp::Size(1998, 1080), dcp::Size(1998, 1080), Eyes::BOTH, Part::WHOLE, boost::optional(), VideoRange::FULL, std::weak_ptr(), boost::optional(), false); + auto dcp_video = make_shared(frame, 0, 24, 100000000, Resolution::TWO_K); + auto j2k = dcp_video->encode_locally(); + BOOST_REQUIRE (j2k.size() >= 65536); +} + + diff --git a/test/wscript b/test/wscript index f47eaea73..70aad81a3 100644 --- a/test/wscript +++ b/test/wscript @@ -94,6 +94,7 @@ def build(bld): j2k_bandwidth_test.cc job_test.cc kdm_naming_test.cc + low_bitrate_test.cc markers_test.cc no_use_video_test.cc optimise_stills_test.cc -- 2.30.2