#include "mid_side_decoder.h"
#include "upmixer_a.h"
#include "upmixer_b.h"
+#include "config.h"
using std::string;
using std::list;
list<AudioProcessor const *> AudioProcessor::_all;
+list<AudioProcessor const *> AudioProcessor::_non_experimental;
void
AudioProcessor::setup_audio_processors ()
{
- _all.push_back (new MidSideDecoder ());
+ AudioProcessor* mid_side = new MidSideDecoder ();
+ _all.push_back (mid_side);
+ _non_experimental.push_back (mid_side);
+
_all.push_back (new UpmixerA (48000));
_all.push_back (new UpmixerB (48000));
}
return 0;
}
+list<AudioProcessor const *>
+AudioProcessor::visible ()
+{
+ if (Config::instance()->show_experimental_audio_processors()) {
+ return _all;
+ }
+
+ return _non_experimental;
+}
+
list<AudioProcessor const *>
AudioProcessor::all ()
{
virtual std::vector<std::string> input_names () const = 0;
static std::list<AudioProcessor const *> all ();
+ static std::list<AudioProcessor const *> visible ();
static void setup_audio_processors ();
static AudioProcessor const * from_id (std::string);
private:
static std::list<AudioProcessor const *> _all;
+ static std::list<AudioProcessor const *> _non_experimental;
};
#endif
_tms_password = "";
_allow_any_dcp_frame_rate = false;
_allow_any_container = false;
+ _show_experimental_audio_processors = false;
_language = optional<string> ();
_default_still_length = 10;
_default_container = Ratio::from_id ("185");
_maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
_allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
_allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
+ _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
_log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
_analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
/* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to standard containers. */
root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
+ /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
+ root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
/* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
to encoding, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
to sending email.
PLAYER_PLAYLIST_DIRECTORY,
PLAYER_DEBUG_LOG,
HISTORY,
+ SHOW_EXPERIMENTAL_AUDIO_PROCESSORS,
#ifdef DCPOMATIC_VARIANT_SWAROOP
PLAYER_BACKGROUND_IMAGE,
#endif
return _allow_any_container;
}
+ bool show_experimental_audio_processors () const {
+ return _show_experimental_audio_processors;
+ }
+
ISDCFMetadata default_isdcf_metadata () const {
return _default_isdcf_metadata;
}
maybe_set (_allow_any_container, a);
}
+ void set_show_experimental_audio_processors (bool e) {
+ maybe_set (_show_experimental_audio_processors, e, SHOW_EXPERIMENTAL_AUDIO_PROCESSORS);
+ }
+
void set_default_isdcf_metadata (ISDCFMetadata d) {
maybe_set (_default_isdcf_metadata, d);
}
https://www.dcpomatic.com/forum/viewtopic.php?f=2&t=1119&p=4468
*/
bool _allow_any_container;
+ /** Offer the upmixers in the audio processor settings */
+ bool _show_experimental_audio_processors;
/** Default ISDCF metadata for newly-created Films */
ISDCFMetadata _default_isdcf_metadata;
boost::optional<std::string> _language;
/*
- Copyright (C) 2014-2018 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2014-2019 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
#include "ffmpeg_image_proxy.h"
#include "image.h"
#include "config.h"
+#include "digester.h"
#include <dcp/dcp.h>
#include <dcp/cpl.h>
#include <dcp/reel.h>
using std::list;
using std::cout;
+using std::string;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
using boost::optional;
}
}
- if (old) {
- _reels = old->_reels;
+ /* We try to avoid re-scanning the DCP's files every time we make a new DCPDecoder; we do this
+ by re-using the _reels list. Before we do this we need to check that nothing too serious
+ has changed in the DCPContent.
- /* We might have gained a KDM since we made the Reel objects */
- if (_dcp_content->kdm ()) {
- dcp::DecryptedKDM k = decrypted_kdm ();
- BOOST_FOREACH (shared_ptr<dcp::Reel> i, _reels) {
- i->add (k);
- }
- }
+ We do this by storing a digest of the important bits of the DCPContent and then checking that's
+ the same before we re-use _reels.
+ */
+
+ _lazy_digest = calculate_lazy_digest (c);
+
+ if (old && old->lazy_digest() == _lazy_digest) {
+ _reels = old->_reels;
} else {
list<shared_ptr<dcp::CPL> > cpl_list = cpls ();
{
_forced_reduction = reduction;
}
+
+string
+DCPDecoder::calculate_lazy_digest (shared_ptr<const DCPContent> c) const
+{
+ Digester d;
+ BOOST_FOREACH (boost::filesystem::path i, c->paths()) {
+ d.add (i.string());
+ }
+ d.add (static_cast<bool>(_dcp_content->kdm()));
+ d.add (static_cast<bool>(c->cpl()));
+ if (c->cpl()) {
+ d.add (c->cpl().get());
+ }
+ return d.get ();
+}
bool pass ();
void seek (dcpomatic::ContentTime t, bool accurate);
+ std::string lazy_digest () const {
+ return _lazy_digest;
+ }
+
private:
friend struct dcp_subtitle_within_dcp_test;
boost::shared_ptr<TextDecoder> decoder,
dcp::Size size
);
+ std::string calculate_lazy_digest (boost::shared_ptr<const DCPContent>) const;
/** Time of next thing to return from pass relative to the start of _reel */
dcpomatic::ContentTime _next;
bool _decode_referenced;
boost::optional<int> _forced_reduction;
+
+ std::string _lazy_digest;
};
using std::back_inserter;
using std::map;
using std::exception;
+using std::find;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::dynamic_pointer_cast;
_audio_processor = 0;
}
+ if (_audio_processor && !Config::instance()->show_experimental_audio_processors()) {
+ list<AudioProcessor const *> ap = AudioProcessor::visible();
+ if (find(ap.begin(), ap.end(), _audio_processor) == ap.end()) {
+ Config::instance()->set_show_experimental_audio_processors(true);
+ }
+ }
+
_reel_type = static_cast<ReelType> (f.optional_number_child<int>("ReelType").get_value_or (static_cast<int>(REELTYPE_SINGLE)));
_reel_length = f.optional_number_child<int64_t>("ReelLength").get_value_or (2000000000);
_upload_after_make_dcp = f.optional_bool_child("UploadAfterMakeDCP").get_value_or (false);
}
if (film->audio_channels() < 6) {
- hint (_("Your DCP has fewer than 6 audio channels. This may cause problems on some projectors."));
+ hint (_("Your DCP has fewer than 6 audio channels. This may cause problems on some projectors. You may want to set the DCP to have 6 channels. It does not matter if your content has fewer channels, as DCP-o-matic will fill the extras with silence."));
}
AudioProcessor const * ap = film->audio_processor();
friend struct player_subframe_test;
friend struct empty_test1;
friend struct empty_test2;
+ friend struct check_reuse_old_data_test;
void setup_pieces ();
void setup_pieces_unlocked ();
_audio_grid->Clear ();
add_audio_panel_to_grid ();
_audio_grid->Layout ();
+ } else if (p == Config::SHOW_EXPERIMENTAL_AUDIO_PROCESSORS) {
+ _audio_processor->Clear ();
+ add_audio_processors ();
+ if (_film) {
+ film_changed (Film::AUDIO_PROCESSOR);
+ }
}
}
DCPPanel::make_audio_panel ()
{
wxPanel* panel = new wxPanel (_notebook);
- wxSizer* sizer = new wxBoxSizer (wxVERTICAL);
+ _audio_panel_sizer = new wxBoxSizer (wxVERTICAL);
_audio_grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
- sizer->Add (_audio_grid, 0, wxALL, 8);
- panel->SetSizer (sizer);
+ _audio_panel_sizer->Add (_audio_grid, 0, wxALL, 8);
+ panel->SetSizer (_audio_panel_sizer);
_channels_label = create_label (panel, _("Channels"), true);
_audio_channels = new wxChoice (panel, wxID_ANY);
_processor_label = create_label (panel, _("Processor"), true);
_audio_processor = new wxChoice (panel, wxID_ANY);
- _audio_processor->Append (_("None"), new wxStringClientData (N_("none")));
- BOOST_FOREACH (AudioProcessor const * ap, AudioProcessor::all ()) {
- _audio_processor->Append (std_to_wx (ap->name ()), new wxStringClientData (std_to_wx (ap->id ())));
- }
+ add_audio_processors ();
_show_audio = new Button (panel, _("Show audio..."));
_film->set_reel_length (_reel_length->GetValue() * 1000000000LL);
}
+
+void
+DCPPanel::add_audio_processors ()
+{
+ _audio_processor->Append (_("None"), new wxStringClientData (N_("none")));
+ BOOST_FOREACH (AudioProcessor const * ap, AudioProcessor::visible()) {
+ _audio_processor->Append (std_to_wx(ap->name()), new wxStringClientData(std_to_wx(ap->id())));
+ }
+ _audio_panel_sizer->Layout();
+}
void add_to_grid ();
void add_video_panel_to_grid ();
void add_audio_panel_to_grid ();
+ void add_audio_processors ();
int minimum_allowed_audio_channels () const;
wxCheckBox* _upload_after_make_dcp;
wxButton* _markers;
wxButton* _metadata;
+ wxSizer* _audio_panel_sizer;
AudioDialog* _audio_dialog;
MarkersDialog* _markers_dialog;
, _maximum_j2k_bandwidth (0)
, _allow_any_dcp_frame_rate (0)
, _allow_any_container (0)
+ , _show_experimental_audio_processors (0)
, _only_servers_encode (0)
, _log_general (0)
, _log_warning (0)
restart->SetFont (font);
table->AddSpacer (0);
+ _show_experimental_audio_processors = new CheckBox (_panel, _("Show experimental audio processors"));
+ table->Add (_show_experimental_audio_processors, 1, wxEXPAND | wxALL);
+ table->AddSpacer (0);
+
_only_servers_encode = new CheckBox (_panel, _("Only servers encode"));
table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
table->AddSpacer (0);
_video_display_mode->Bind (wxEVT_CHOICE, boost::bind(&AdvancedPage::video_display_mode_changed, this));
_allow_any_dcp_frame_rate->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
_allow_any_container->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::allow_any_container_changed, this));
+ _show_experimental_audio_processors->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::show_experimental_audio_processors_changed, this));
_only_servers_encode->Bind (wxEVT_CHECKBOX, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
_frames_in_memory_multiplier->Bind (wxEVT_SPINCTRL, boost::bind(&AdvancedPage::frames_in_memory_multiplier_changed, this));
_dcp_metadata_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_metadata_filename_format_changed, this));
}
checked_set (_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate ());
checked_set (_allow_any_container, config->allow_any_container ());
+ checked_set (_show_experimental_audio_processors, config->show_experimental_audio_processors ());
checked_set (_only_servers_encode, config->only_servers_encode ());
checked_set (_log_general, config->log_types() & LogEntry::TYPE_GENERAL);
checked_set (_log_warning, config->log_types() & LogEntry::TYPE_WARNING);
Config::instance()->set_allow_any_container (_allow_any_container->GetValue ());
}
+ void show_experimental_audio_processors_changed ()
+ {
+ Config::instance()->set_show_experimental_audio_processors (_show_experimental_audio_processors->GetValue ());
+ }
+
void only_servers_encode_changed ()
{
Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
wxSpinCtrl* _frames_in_memory_multiplier;
wxCheckBox* _allow_any_dcp_frame_rate;
wxCheckBox* _allow_any_container;
+ wxCheckBox* _show_experimental_audio_processors;
wxCheckBox* _only_servers_encode;
NameFormatEditor* _dcp_metadata_filename_format;
NameFormatEditor* _dcp_asset_filename_format;
--- /dev/null
+/*
+ Copyright (C) 2019 Carl Hetherington <cth@carlh.net>
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+/** @file test/dcp_decoder_test.cc
+ * @brief Test DCPDecoder class.
+ * @ingroup selfcontained
+ */
+
+#include "lib/film.h"
+#include "lib/dcp_content.h"
+#include "lib/dcp_decoder.h"
+#include "lib/content_factory.h"
+#include "lib/player.h"
+#include "lib/examine_content_job.h"
+#include "lib/job_manager.h"
+#include "lib/config.h"
+#include "test.h"
+#include <dcp/dcp.h>
+#include <boost/test/unit_test.hpp>
+#include <iostream>
+
+using std::list;
+using std::string;
+using std::vector;
+using boost::shared_ptr;
+
+/* Check that DCPDecoder reuses old data when it should */
+BOOST_AUTO_TEST_CASE (check_reuse_old_data_test)
+{
+ /* Make some DCPs */
+
+ shared_ptr<Film> ov = new_test_film2 ("check_reuse_old_data_ov");
+ ov->examine_and_add_content (content_factory("test/data/flat_red.png").front());
+ BOOST_REQUIRE (!wait_for_jobs());
+ ov->make_dcp ();
+ BOOST_REQUIRE (!wait_for_jobs());
+
+ shared_ptr<Film> vf = new_test_film2 ("check_reuse_old_data_vf");
+ shared_ptr<DCPContent> ov_content(new DCPContent(ov->dir(ov->dcp_name(false))));
+ vf->examine_and_add_content (ov_content);
+ vf->examine_and_add_content (content_factory("test/data/L.wav").front());
+ BOOST_REQUIRE (!wait_for_jobs());
+ ov_content->set_reference_video (true);
+ vf->make_dcp ();
+ BOOST_REQUIRE (!wait_for_jobs());
+
+ shared_ptr<Film> encrypted = new_test_film2 ("check_reuse_old_data_decrypted");
+ encrypted->examine_and_add_content (content_factory("test/data/flat_red.png").front());
+ BOOST_REQUIRE (!wait_for_jobs());
+ encrypted->set_encrypted (true);
+ encrypted->make_dcp ();
+ BOOST_REQUIRE (!wait_for_jobs());
+
+ dcp::DCP encrypted_dcp (encrypted->dir(encrypted->dcp_name()));
+ encrypted_dcp.read ();
+
+ dcp::EncryptedKDM kdm = encrypted->make_kdm (
+ Config::instance()->decryption_chain()->leaf(),
+ vector<string>(),
+ encrypted_dcp.cpls().front()->file().get(),
+ dcp::LocalTime ("2014-07-21T00:00:00+00:00"),
+ dcp::LocalTime ("2024-07-21T00:00:00+00:00"),
+ dcp::MODIFIED_TRANSITIONAL_1,
+ true, 0
+ );
+
+
+ /* Add just the OV to a new project, move it around a bit and check that
+ the _reels get reused.
+ */
+ shared_ptr<Film> test = new_test_film2 ("check_reuse_old_data_test1");
+ ov_content.reset (new DCPContent(ov->dir(ov->dcp_name(false))));
+ test->examine_and_add_content (ov_content);
+ BOOST_REQUIRE (!wait_for_jobs());
+ shared_ptr<Player> player (new Player(test, test->playlist()));
+
+ shared_ptr<DCPDecoder> decoder = boost::dynamic_pointer_cast<DCPDecoder>(player->_pieces.front()->decoder);
+ BOOST_REQUIRE (decoder);
+ list<shared_ptr<dcp::Reel> > reels = decoder->reels();
+
+ ov_content->set_position (test, dcpomatic::DCPTime(96000));
+ decoder = boost::dynamic_pointer_cast<DCPDecoder>(player->_pieces.front()->decoder);
+ BOOST_REQUIRE (decoder);
+ BOOST_REQUIRE (reels == decoder->reels());
+
+ /* Add the VF to a new project, then add the OV and check that the
+ _reels did not get reused.
+ */
+ test = new_test_film2 ("check_reuse_old_data_test2");
+ shared_ptr<DCPContent> vf_content (new DCPContent(vf->dir(vf->dcp_name(false))));
+ test->examine_and_add_content (vf_content);
+ BOOST_REQUIRE (!wait_for_jobs());
+ player.reset (new Player(test, test->playlist()));
+
+ decoder = boost::dynamic_pointer_cast<DCPDecoder>(player->_pieces.front()->decoder);
+ BOOST_REQUIRE (decoder);
+ reels = decoder->reels();
+
+ vf_content->add_ov (ov->dir(ov->dcp_name(false)));
+ JobManager::instance()->add (shared_ptr<Job>(new ExamineContentJob(test, vf_content)));
+ BOOST_REQUIRE (!wait_for_jobs());
+ decoder = boost::dynamic_pointer_cast<DCPDecoder>(player->_pieces.front()->decoder);
+ BOOST_REQUIRE (decoder);
+ BOOST_REQUIRE (reels != decoder->reels());
+
+ /* Add a KDM to an encrypted DCP and check that the _reels did not get reused */
+ test = new_test_film2 ("check_reuse_old_data_test3");
+ shared_ptr<DCPContent> encrypted_content (new DCPContent(encrypted->dir(encrypted->dcp_name(false))));
+ test->examine_and_add_content (encrypted_content);
+ BOOST_REQUIRE (!wait_for_jobs());
+ player.reset (new Player(test, test->playlist()));
+
+ decoder = boost::dynamic_pointer_cast<DCPDecoder>(player->_pieces.front()->decoder);
+ BOOST_REQUIRE (decoder);
+ reels = decoder->reels();
+
+ encrypted_content->add_kdm (kdm);
+ JobManager::instance()->add (shared_ptr<Job>(new ExamineContentJob(test, encrypted_content)));
+ BOOST_REQUIRE (!wait_for_jobs());
+ decoder = boost::dynamic_pointer_cast<DCPDecoder>(player->_pieces.front()->decoder);
+ BOOST_REQUIRE (decoder);
+ BOOST_REQUIRE (reels != decoder->reels());
+}
create_cli_test.cc
crypto_test.cc
dcpomatic_time_test.cc
+ dcp_decoder_test.cc
dcp_playback_test.cc
dcp_subtitle_test.cc
digest_test.cc