From ca981c8cfa23111e92be329f1c2dfbe3a07b4247 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 19 Jun 2020 13:30:02 +0200 Subject: [PATCH] Make Atmos content work more like other content. Now its MXFs are re-written, meaning that they can be encrypted. This (along with the libdcp update) also fixes assorted Atmos bugs. --- cscript | 4 +- src/lib/atmos_content.cc | 77 ++++++++++++++++++++++++++++++ src/lib/atmos_content.h | 62 ++++++++++++++++++++++++ src/lib/atmos_decoder.cc | 54 +++++++++++++++++++++ src/lib/atmos_decoder.h | 46 ++++++++++++++++++ src/lib/atmos_metadata.cc | 43 +++++++++++++++++ src/lib/atmos_metadata.h | 41 ++++++++++++++++ src/lib/atmos_mxf_content.cc | 16 +++---- src/lib/atmos_mxf_content.h | 3 -- src/lib/atmos_mxf_decoder.cc | 65 ++++++++++++++++++++++++++ src/lib/atmos_mxf_decoder.h | 42 +++++++++++++++++ src/lib/content.h | 2 + src/lib/content_atmos.h | 46 ++++++++++++++++++ src/lib/dcp_content.cc | 6 +++ src/lib/dcp_decoder.cc | 22 ++++++++- src/lib/dcp_decoder.h | 5 +- src/lib/dcp_encoder.cc | 10 ++++ src/lib/dcp_encoder.h | 6 ++- src/lib/dcp_examiner.cc | 15 ++++++ src/lib/dcp_examiner.h | 5 ++ src/lib/decoder.h | 4 +- src/lib/decoder_factory.cc | 9 +++- src/lib/film.cc | 28 +++++++++++ src/lib/film.h | 3 ++ src/lib/player.cc | 19 ++++++-- src/lib/player.h | 9 +++- src/lib/reel_writer.cc | 46 ++++++++++++++++++ src/lib/reel_writer.h | 8 +++- src/lib/util.cc | 16 +++++++ src/lib/util.h | 2 + src/lib/video_mxf_decoder.cc | 1 - src/lib/writer.cc | 14 ++++++ src/lib/writer.h | 6 ++- src/lib/wscript | 4 ++ src/wx/dcp_panel.cc | 12 +++-- test/atmos_test.cc | 91 ++++++++++++++++++++++++++++++++++++ test/test.cc | 77 ++++++++++++++++++++++++++++++ test/test.h | 1 + test/wscript | 1 + 39 files changed, 891 insertions(+), 30 deletions(-) create mode 100644 src/lib/atmos_content.cc create mode 100644 src/lib/atmos_content.h create mode 100644 src/lib/atmos_decoder.cc create mode 100644 src/lib/atmos_decoder.h create mode 100644 src/lib/atmos_metadata.cc create mode 100644 src/lib/atmos_metadata.h create mode 100644 src/lib/atmos_mxf_decoder.cc create mode 100644 src/lib/atmos_mxf_decoder.h create mode 100644 src/lib/content_atmos.h create mode 100644 test/atmos_test.cc diff --git a/cscript b/cscript index 6966864a6..864ec90c2 100644 --- a/cscript +++ b/cscript @@ -375,8 +375,8 @@ def dependencies(target, options): (target.platform == 'osx' and target.bits == 64) or (target.platform == 'windows')) else {} - deps.append(('libdcp', 'cdb664d', cpp_lib_options)) - deps.append(('libsub', 'dec1726', cpp_lib_options)) + deps.append(('libdcp', '2667081', cpp_lib_options)) + deps.append(('libsub', 'fc5ce01', cpp_lib_options)) deps.append(('leqm-nrt', 'carl')) deps.append(('rtaudio', 'carl')) # We get our OpenSSL libraries from the environment, but we diff --git a/src/lib/atmos_content.cc b/src/lib/atmos_content.cc new file mode 100644 index 000000000..c424f96d3 --- /dev/null +++ b/src/lib/atmos_content.cc @@ -0,0 +1,77 @@ +/* + Copyright (C) 2020 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 "atmos_content.h" +#include +#include + + +using std::string; +using boost::shared_ptr; + + +int const AtmosContentProperty::EDIT_RATE = 700; + + +AtmosContent::AtmosContent (Content* parent) + : ContentPart (parent) + , _length (0) +{ + +} + + +AtmosContent::AtmosContent (Content* parent, cxml::ConstNodePtr node) + : ContentPart (parent) +{ + _length = node->number_child("Length"); + _edit_rate = dcp::Fraction (node->string_child("EditRate")); +} + + +shared_ptr +AtmosContent::from_xml (Content* parent, cxml::ConstNodePtr node) +{ + return shared_ptr (new AtmosContent(parent, node)); +} + + +void +AtmosContent::as_xml (xmlpp::Node* node) const +{ + node->add_child("Length")->add_child_text(dcp::raw_convert(_length)); + node->add_child("EditRate")->add_child_text(_edit_rate.as_string()); +} + + +void +AtmosContent::set_length (Frame len) +{ + maybe_set (_length, len, ContentProperty::LENGTH); +} + + +void +AtmosContent::set_edit_rate (dcp::Fraction rate) +{ + maybe_set (_edit_rate, rate, AtmosContentProperty::EDIT_RATE); +} + diff --git a/src/lib/atmos_content.h b/src/lib/atmos_content.h new file mode 100644 index 000000000..0344b6225 --- /dev/null +++ b/src/lib/atmos_content.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2020 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 "content_part.h" + + +class Content; + + +class AtmosContentProperty +{ +public: + static int const EDIT_RATE; +}; + + +class AtmosContent : public ContentPart +{ +public: + AtmosContent (Content* parent); + + static boost::shared_ptr from_xml (Content* parent, cxml::ConstNodePtr node); + + void as_xml (xmlpp::Node* node) const; + + void set_length (Frame len); + + Frame length () const { + return _length; + } + + void set_edit_rate (dcp::Fraction rate); + + dcp::Fraction edit_rate () const { + return _edit_rate; + } + +private: + AtmosContent (Content* parent, cxml::ConstNodePtr node); + + Frame _length; + dcp::Fraction _edit_rate; +}; + diff --git a/src/lib/atmos_decoder.cc b/src/lib/atmos_decoder.cc new file mode 100644 index 000000000..4ebb07f8a --- /dev/null +++ b/src/lib/atmos_decoder.cc @@ -0,0 +1,54 @@ +/* + Copyright (C) 2020 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 "atmos_decoder.h" +#include "content.h" +#include "dcpomatic_assert.h" +#include "dcpomatic_time.h" +#include "decoder.h" +#include "film.h" + + +using boost::shared_ptr; + + +AtmosDecoder::AtmosDecoder (Decoder* parent, shared_ptr content) + : DecoderPart (parent) + , _content (content) +{ + +} + + +void +AtmosDecoder::seek () +{ + _position = boost::none; +} + + +void +AtmosDecoder::emit (shared_ptr film, shared_ptr data, Frame frame, AtmosMetadata metadata) +{ + Data (ContentAtmos(data, frame, metadata)); + /* There's no fiddling with frame rates when we are using Atmos; the DCP rate must be the same as the Atmos one */ + _position = dcpomatic::ContentTime::from_frames (frame, film->video_frame_rate()); +} diff --git a/src/lib/atmos_decoder.h b/src/lib/atmos_decoder.h new file mode 100644 index 000000000..3003d5a07 --- /dev/null +++ b/src/lib/atmos_decoder.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2020 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 "atmos_metadata.h" +#include "content_atmos.h" +#include "decoder_part.h" +#include + + +class AtmosDecoder : public DecoderPart +{ +public: + AtmosDecoder (Decoder* parent, boost::shared_ptr content); + + boost::optional position (boost::shared_ptr) const { + return _position; + } + + void seek (); + + void emit (boost::shared_ptr film, boost::shared_ptr data, Frame frame, AtmosMetadata metadata); + + boost::signals2::signal Data; + +private: + boost::shared_ptr _content; + boost::optional _position; +}; diff --git a/src/lib/atmos_metadata.cc b/src/lib/atmos_metadata.cc new file mode 100644 index 000000000..0a4b4f1b1 --- /dev/null +++ b/src/lib/atmos_metadata.cc @@ -0,0 +1,43 @@ +/* + Copyright (C) 2012-2020 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 "atmos_metadata.h" +#include + + +using boost::shared_ptr; + + +AtmosMetadata::AtmosMetadata (shared_ptr asset) + : _first_frame (asset->first_frame()) + , _max_channel_count (asset->max_channel_count()) + , _max_object_count (asset->max_object_count()) + , _atmos_version (asset->atmos_version()) +{ + +} + + +shared_ptr +AtmosMetadata::create (dcp::Fraction edit_rate) const +{ + return shared_ptr (new dcp::AtmosAsset(edit_rate, _first_frame, _max_channel_count, _max_object_count, _atmos_version)); +} diff --git a/src/lib/atmos_metadata.h b/src/lib/atmos_metadata.h new file mode 100644 index 000000000..4e3fc855f --- /dev/null +++ b/src/lib/atmos_metadata.h @@ -0,0 +1,41 @@ +/* + Copyright (C) 2012-2020 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 . + +*/ + +#ifndef DCPOMATIC_ATMOS_METADATA_H +#define DCPOMATIC_ATMOS_METADATA_H + +#include +#include + +class AtmosMetadata +{ +public: + AtmosMetadata (boost::shared_ptr asset); + boost::shared_ptr create (dcp::Fraction edit_rate) const; + +private: + int _first_frame; + int _max_channel_count; + int _max_object_count; + int _atmos_version; +}; + +#endif + diff --git a/src/lib/atmos_mxf_content.cc b/src/lib/atmos_mxf_content.cc index 4c1201c70..36ae8871a 100644 --- a/src/lib/atmos_mxf_content.cc +++ b/src/lib/atmos_mxf_content.cc @@ -18,6 +18,7 @@ */ +#include "atmos_content.h" #include "atmos_mxf_content.h" #include "job.h" #include "film.h" @@ -44,10 +45,7 @@ AtmosMXFContent::AtmosMXFContent (boost::filesystem::path path) AtmosMXFContent::AtmosMXFContent (cxml::ConstNodePtr node, int) : Content (node) { - /* This was mistakenly left out for a while, so make sure we at least don't - * crash if an old Film is loaded. - */ - _length = node->optional_number_child("Length").get_value_or(0); + atmos = AtmosContent::from_xml (this, node); } bool @@ -78,7 +76,9 @@ AtmosMXFContent::examine (shared_ptr film, shared_ptr job) { boost::mutex::scoped_lock lm (_mutex); - _length = a->intrinsic_duration (); + atmos.reset (new AtmosContent(this)); + atmos->set_length (a->intrinsic_duration()); + atmos->set_edit_rate (a->edit_rate()); } } @@ -93,18 +93,18 @@ AtmosMXFContent::as_xml (xmlpp::Node* node, bool with_paths) const { node->add_child("Type")->add_child_text ("AtmosMXF"); Content::as_xml (node, with_paths); - node->add_child("Length")->add_child_text(dcp::raw_convert(_length)); + atmos->as_xml (node); } DCPTime AtmosMXFContent::full_length (shared_ptr film) const { FrameRateChange const frc (film, shared_from_this()); - return DCPTime::from_frames (llrint (_length * frc.factor()), film->video_frame_rate()); + return DCPTime::from_frames (llrint (atmos->length() * frc.factor()), film->video_frame_rate()); } DCPTime AtmosMXFContent::approximate_length () const { - return DCPTime::from_frames (_length, 24); + return DCPTime::from_frames (atmos->length(), 24); } diff --git a/src/lib/atmos_mxf_content.h b/src/lib/atmos_mxf_content.h index 06e9e747d..350c4a35d 100644 --- a/src/lib/atmos_mxf_content.h +++ b/src/lib/atmos_mxf_content.h @@ -41,7 +41,4 @@ public: dcpomatic::DCPTime approximate_length () const; static bool valid_mxf (boost::filesystem::path path); - -private: - Frame _length; }; diff --git a/src/lib/atmos_mxf_decoder.cc b/src/lib/atmos_mxf_decoder.cc new file mode 100644 index 000000000..3f6f4a857 --- /dev/null +++ b/src/lib/atmos_mxf_decoder.cc @@ -0,0 +1,65 @@ +/* + Copyright (C) 2020 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 "atmos_content.h" +#include "atmos_decoder.h" +#include "atmos_mxf_content.h" +#include "atmos_mxf_decoder.h" +#include "dcpomatic_time.h" +#include +#include + +using boost::shared_ptr; + +AtmosMXFDecoder::AtmosMXFDecoder (boost::shared_ptr film, boost::shared_ptr content) + : Decoder (film) + , _content (content) +{ + atmos.reset (new AtmosDecoder(this, content)); + + shared_ptr asset (new dcp::AtmosAsset(_content->path(0))); + _reader = asset->start_read (); + _metadata = AtmosMetadata (asset); +} + + +bool +AtmosMXFDecoder::pass () +{ + double const vfr = _content->active_video_frame_rate (film()); + int64_t const frame = _next.frames_round (vfr); + + if (frame >= _content->atmos->length()) { + return true; + } + + atmos->emit (film(), _reader->get_frame(frame), frame, *_metadata); + _next += dcpomatic::ContentTime::from_frames (1, vfr); + return false; +} + + +void +AtmosMXFDecoder::seek (dcpomatic::ContentTime t, bool accurate) +{ + Decoder::seek (t, accurate); + _next = t; +} + diff --git a/src/lib/atmos_mxf_decoder.h b/src/lib/atmos_mxf_decoder.h new file mode 100644 index 000000000..f5b7fe1ee --- /dev/null +++ b/src/lib/atmos_mxf_decoder.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2020 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 "atmos_metadata.h" +#include "dcpomatic_time.h" +#include "decoder.h" +#include + +class AtmosMXFContent; + +class AtmosMXFDecoder : public Decoder +{ +public: + AtmosMXFDecoder (boost::shared_ptr film, boost::shared_ptr); + + bool pass (); + void seek (dcpomatic::ContentTime t, bool accurate); + +private: + boost::shared_ptr _content; + dcpomatic::ContentTime _next; + boost::shared_ptr _reader; + boost::optional _metadata; +}; + diff --git a/src/lib/content.h b/src/lib/content.h index a2d78aa68..5f8e9f53d 100644 --- a/src/lib/content.h +++ b/src/lib/content.h @@ -46,6 +46,7 @@ namespace cxml { class Job; class Film; +class AtmosContent; class ContentProperty { @@ -190,6 +191,7 @@ public: boost::shared_ptr video; boost::shared_ptr audio; std::list > text; + boost::shared_ptr atmos; boost::shared_ptr only_text () const; boost::shared_ptr text_of_original_type (TextType type) const; diff --git a/src/lib/content_atmos.h b/src/lib/content_atmos.h new file mode 100644 index 000000000..57b7e404f --- /dev/null +++ b/src/lib/content_atmos.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2020 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 . + +*/ + +#ifndef DCPOMATIC_CONTENT_ATMOS_H +#define DCPOMATIC_CONTENT_ATMOS_H + +#include "atmos_metadata.h" +#include "types.h" +#include +#include + +/** @class ContentAtmos + * @brief Some Atmos data that has come out of a decoder. + */ +class ContentAtmos +{ +public: + ContentAtmos (boost::shared_ptr data_, Frame frame_, AtmosMetadata metadata_) + : data (data_) + , frame (frame_) + , metadata (metadata_) + {} + + boost::shared_ptr data; + Frame frame; + AtmosMetadata metadata; +}; + +#endif diff --git a/src/lib/dcp_content.cc b/src/lib/dcp_content.cc index ca210b558..d2c1df748 100644 --- a/src/lib/dcp_content.cc +++ b/src/lib/dcp_content.cc @@ -18,6 +18,7 @@ */ +#include "atmos_content.h" #include "dcp_content.h" #include "video_content.h" #include "audio_content.h" @@ -233,6 +234,11 @@ DCPContent::examine (shared_ptr film, shared_ptr job) as->set_mapping (m); } + if (examiner->has_atmos()) { + boost::mutex::scoped_lock lm (_mutex); + atmos.reset (new AtmosContent(this)); + } + int texts = 0; { boost::mutex::scoped_lock lm (_mutex); diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc index ab724cd6c..30e337252 100644 --- a/src/lib/dcp_decoder.cc +++ b/src/lib/dcp_decoder.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2014-2019 Carl Hetherington + Copyright (C) 2014-2020 Carl Hetherington This file is part of DCP-o-matic. @@ -18,6 +18,7 @@ */ +#include "atmos_decoder.h" #include "dcp_decoder.h" #include "dcp_content.h" #include "audio_content.h" @@ -47,6 +48,7 @@ #include #include #include +#include #include #include @@ -76,6 +78,9 @@ DCPDecoder::DCPDecoder (shared_ptr film, shared_ptr (new TextDecoder (this, i, ContentTime()))); } + if (c->atmos) { + atmos.reset (new AtmosDecoder (this, c)); + } } /* We try to avoid re-scanning the DCP's files every time we make a new DCPDecoder; we do this @@ -215,6 +220,11 @@ DCPDecoder::pass () audio->emit (film(), _dcp_content->audio->stream(), data, ContentTime::from_frames (_offset, vfr) + _next); } + if (_atmos_reader) { + DCPOMATIC_ASSERT (_atmos_metadata); + atmos->emit (film(), _atmos_reader->get_frame(frame), frame, *_atmos_metadata); + } + _next += ContentTime::from_frames (1, vfr); if ((*_reel)->main_picture ()) { @@ -341,6 +351,7 @@ DCPDecoder::get_readers () _mono_reader.reset (); _stereo_reader.reset (); _sound_reader.reset (); + _atmos_reader.reset (); return; } @@ -366,6 +377,15 @@ DCPDecoder::get_readers () } else { _sound_reader.reset (); } + + if ((*_reel)->atmos()) { + shared_ptr asset = (*_reel)->atmos()->asset(); + _atmos_reader = asset->start_read(); + _atmos_metadata = AtmosMetadata (asset); + } else { + _atmos_reader.reset (); + _atmos_metadata = boost::none; + } } void diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h index 4de8c8629..1abe39a27 100644 --- a/src/lib/dcp_decoder.h +++ b/src/lib/dcp_decoder.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2014-2016 Carl Hetherington + Copyright (C) 2014-2020 Carl Hetherington This file is part of DCP-o-matic. @@ -22,6 +22,7 @@ * @brief A decoder of existing DCPs. */ +#include "atmos_metadata.h" #include "decoder.h" #include "dcp.h" #include @@ -93,6 +94,8 @@ private: boost::shared_ptr _stereo_reader; /** Reader for current sound asset, if applicable */ boost::shared_ptr _sound_reader; + boost::shared_ptr _atmos_reader; + boost::optional _atmos_metadata; bool _decode_referenced; boost::optional _forced_reduction; diff --git a/src/lib/dcp_encoder.cc b/src/lib/dcp_encoder.cc index d17c6c985..438a73fd6 100644 --- a/src/lib/dcp_encoder.cc +++ b/src/lib/dcp_encoder.cc @@ -64,6 +64,7 @@ DCPEncoder::DCPEncoder (shared_ptr film, weak_ptr job) _player_video_connection = _player->Video.connect (bind (&DCPEncoder::video, this, _1, _2)); _player_audio_connection = _player->Audio.connect (bind (&DCPEncoder::audio, this, _1, _2)); _player_text_connection = _player->Text.connect (bind (&DCPEncoder::text, this, _1, _2, _3, _4)); + _player_atmos_connection = _player->Atmos.connect (bind (&DCPEncoder::atmos, this, _1, _2, _3)); BOOST_FOREACH (shared_ptr c, film->content ()) { BOOST_FOREACH (shared_ptr i, c->text) { @@ -80,6 +81,7 @@ DCPEncoder::~DCPEncoder () _player_video_connection.release (); _player_audio_connection.release (); _player_text_connection.release (); + _player_atmos_connection.release (); } void @@ -157,6 +159,14 @@ DCPEncoder::text (PlayerText data, TextType type, optional track, } } + +void +DCPEncoder::atmos (shared_ptr data, DCPTime time, AtmosMetadata metadata) +{ + _writer->write (data, time, metadata); +} + + optional DCPEncoder::current_rate () const { diff --git a/src/lib/dcp_encoder.h b/src/lib/dcp_encoder.h index cc2de3e0c..e5df30fb5 100644 --- a/src/lib/dcp_encoder.h +++ b/src/lib/dcp_encoder.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2018 Carl Hetherington + Copyright (C) 2012-2020 Carl Hetherington This file is part of DCP-o-matic. @@ -18,10 +18,12 @@ */ +#include "atmos_metadata.h" #include "types.h" #include "player_text.h" #include "dcp_text_track.h" #include "encoder.h" +#include #include class Film; @@ -54,6 +56,7 @@ private: void video (boost::shared_ptr, dcpomatic::DCPTime); void audio (boost::shared_ptr, dcpomatic::DCPTime); void text (PlayerText, TextType, boost::optional, dcpomatic::DCPTimePeriod); + void atmos (boost::shared_ptr, dcpomatic::DCPTime, AtmosMetadata metadata); boost::shared_ptr _writer; boost::shared_ptr _j2k_encoder; @@ -63,4 +66,5 @@ private: boost::signals2::scoped_connection _player_video_connection; boost::signals2::scoped_connection _player_audio_connection; boost::signals2::scoped_connection _player_text_connection; + boost::signals2::scoped_connection _player_atmos_connection; }; diff --git a/src/lib/dcp_examiner.cc b/src/lib/dcp_examiner.cc index d04dacdd6..133c1614e 100644 --- a/src/lib/dcp_examiner.cc +++ b/src/lib/dcp_examiner.cc @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,7 @@ DCPExaminer::DCPExaminer (shared_ptr content, bool tolerant) , _needs_assets (false) , _kdm_valid (false) , _three_d (false) + , _has_atmos (false) { shared_ptr cpl; @@ -96,6 +98,9 @@ DCPExaminer::DCPExaminer (shared_ptr content, bool tolerant) if (j->main_subtitle() && !j->main_subtitle()->asset_ref().resolved()) { ++unsatisfied; } + if (j->atmos() && !j->atmos()->asset_ref().resolved()) { + ++unsatisfied; + } } if (unsatisfied < least_unsatisfied) { @@ -191,6 +196,10 @@ DCPExaminer::DCPExaminer (shared_ptr content, bool tolerant) _markers.insert (rm.begin(), rm.end()); } + if (i->atmos()) { + _has_atmos = true; + } + if (i->main_picture()) { _reel_lengths.push_back (i->main_picture()->actual_duration()); } else if (i->main_sound()) { @@ -199,6 +208,8 @@ DCPExaminer::DCPExaminer (shared_ptr content, bool tolerant) _reel_lengths.push_back (i->main_subtitle()->actual_duration()); } else if (!i->closed_captions().empty()) { _reel_lengths.push_back (i->closed_captions().front()->actual_duration()); + } else if (!i->atmos()) { + _reel_lengths.push_back (i->atmos()->actual_duration()); } } @@ -226,6 +237,10 @@ DCPExaminer::DCPExaminer (shared_ptr content, bool tolerant) if (i->main_subtitle()) { i->main_subtitle()->asset()->subtitles (); } + + if (i->atmos()) { + i->atmos()->asset()->start_read()->get_frame(0); + } } } catch (dcp::ReadError& e) { _kdm_valid = false; diff --git a/src/lib/dcp_examiner.h b/src/lib/dcp_examiner.h index 21aa8d3ab..b4c87596c 100644 --- a/src/lib/dcp_examiner.h +++ b/src/lib/dcp_examiner.h @@ -131,6 +131,10 @@ public: return _content_version; } + bool has_atmos () const { + return _has_atmos; + } + private: boost::optional _video_frame_rate; boost::optional _video_size; @@ -156,4 +160,5 @@ private: std::map _markers; std::vector _ratings; std::string _content_version; + bool _has_atmos; }; diff --git a/src/lib/decoder.h b/src/lib/decoder.h index ddea58aac..316109ebb 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2018 Carl Hetherington + Copyright (C) 2012-2020 Carl Hetherington This file is part of DCP-o-matic. @@ -34,6 +34,7 @@ class Decoded; class VideoDecoder; class AudioDecoder; class TextDecoder; +class AtmosDecoder; class DecoderPart; /** @class Decoder. @@ -48,6 +49,7 @@ public: boost::shared_ptr video; boost::shared_ptr audio; std::list > text; + boost::shared_ptr atmos; boost::shared_ptr only_text () const; diff --git a/src/lib/decoder_factory.cc b/src/lib/decoder_factory.cc index 2ded95fef..12e3f3ddd 100644 --- a/src/lib/decoder_factory.cc +++ b/src/lib/decoder_factory.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2016-2019 Carl Hetherington + Copyright (C) 2016-2020 Carl Hetherington This file is part of DCP-o-matic. @@ -18,6 +18,8 @@ */ +#include "atmos_mxf_content.h" +#include "atmos_mxf_decoder.h" #include "ffmpeg_content.h" #include "ffmpeg_decoder.h" #include "dcp_content.h" @@ -89,5 +91,10 @@ decoder_factory (shared_ptr film, shared_ptr content, return shared_ptr (new VideoMXFDecoder(film, vmc)); } + shared_ptr amc = dynamic_pointer_cast (content); + if (amc) { + return shared_ptr (new AtmosMXFDecoder(film, amc)); + } + return shared_ptr (); } diff --git a/src/lib/film.cc b/src/lib/film.cc index c04f2996f..dafe158b0 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -23,6 +23,7 @@ * how they should be presented in a DCP. */ +#include "atmos_content.h" #include "film.h" #include "job.h" #include "util.h" @@ -1397,6 +1398,21 @@ Film::playlist_change (ChangeType type) void Film::check_settings_consistency () { + optional atmos_rate; + BOOST_FOREACH (shared_ptr i, content()) { + + if (i->atmos) { + int rate = lrintf (i->atmos->edit_rate().as_float()); + if (atmos_rate && *atmos_rate != rate) { + Message (_("You have more than one piece of Atmos content, and they do not have the same frame rate. You must remove some Atmos content.")); + } else if (!atmos_rate && rate != video_frame_rate()) { + atmos_rate = rate; + set_video_frame_rate (rate, false); + Message (_("DCP-o-matic had to change your settings so that the film's frame rate is the same as that of your Atmos content.")); + } + } + } + bool change_made = false; BOOST_FOREACH (shared_ptr i, content()) { shared_ptr d = dynamic_pointer_cast(i); @@ -1779,6 +1795,18 @@ Film::references_dcp_audio () const return false; } + +bool +Film::contains_atmos_content () const +{ + BOOST_FOREACH (shared_ptr i, _playlist->content()) { + if (i->atmos) { + return true; + } + } +} + + list Film::closed_caption_tracks () const { diff --git a/src/lib/film.h b/src/lib/film.h index 64bb159c7..f5c20bccd 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -62,6 +62,7 @@ class Job; class Film; struct isdcf_name_test; struct recover_test_2d_encrypted; +struct atmos_encrypted_passthrough_test; class InfoFileHandle { @@ -189,6 +190,7 @@ public: bool references_dcp_video () const; bool references_dcp_audio () const; + bool contains_atmos_content () const; void set_tolerant (bool t) { _tolerant = t; @@ -381,6 +383,7 @@ private: friend struct ::isdcf_name_test; friend struct ::recover_test_2d_encrypted; + friend struct ::atmos_encrypted_passthrough_test; template friend class ChangeSignaller; boost::filesystem::path info_file (dcpomatic::DCPTimePeriod p) const; diff --git a/src/lib/player.cc b/src/lib/player.cc index fc821d6c7..b45401fa9 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -18,6 +18,7 @@ */ +#include "atmos_decoder.h" #include "player.h" #include "film.h" #include "audio_buffers.h" @@ -193,12 +194,9 @@ Player::setup_pieces_unlocked () } shared_ptr decoder = decoder_factory (_film, i, _fast, _tolerant, old_decoder); - FrameRateChange frc (_film, i); + DCPOMATIC_ASSERT (decoder); - if (!decoder) { - /* Not something that we can decode; e.g. Atmos content */ - continue; - } + FrameRateChange frc (_film, i); if (decoder->video && _ignore_video) { decoder->video->set_ignore (true); @@ -253,6 +251,10 @@ Player::setup_pieces_unlocked () ++j; } + + if (decoder->atmos) { + decoder->atmos->Data.connect (bind(&Player::atmos, this, weak_ptr(piece), _1)); + } } _stream_states.clear (); @@ -1270,3 +1272,10 @@ Player::playlist () const return _playlist ? _playlist : _film->playlist(); } + +void +Player::atmos (weak_ptr, ContentAtmos data) +{ + Atmos (data.data, DCPTime::from_frames(data.frame, _film->video_frame_rate()), data.metadata); +} + diff --git a/src/lib/player.h b/src/lib/player.h index bb2a0c613..ea81ae939 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2019 Carl Hetherington + Copyright (C) 2013-2020 Carl Hetherington This file is part of DCP-o-matic. @@ -21,11 +21,13 @@ #ifndef DCPOMATIC_PLAYER_H #define DCPOMATIC_PLAYER_H +#include "atmos_metadata.h" #include "player_text.h" #include "active_text.h" #include "content_text.h" #include "film.h" #include "content.h" +#include "content_atmos.h" #include "position_image.h" #include "piece.h" #include "content_video.h" @@ -46,6 +48,7 @@ namespace dcpomatic { class Font; } +class AtmosContent; class PlayerVideo; class Playlist; class AudioBuffers; @@ -103,6 +106,7 @@ public: * after the corresponding Video. */ boost::signals2::signal, dcpomatic::DCPTimePeriod)> Text; + boost::signals2::signal, dcpomatic::DCPTime, AtmosMetadata)> Atmos; private: friend class PlayerWrapper; @@ -129,11 +133,14 @@ private: dcpomatic::ContentTime dcp_to_content_time (boost::shared_ptr piece, dcpomatic::DCPTime t) const; dcpomatic::DCPTime content_time_to_dcp (boost::shared_ptr piece, dcpomatic::ContentTime t) const; boost::shared_ptr black_player_video_frame (Eyes eyes) const; + void video (boost::weak_ptr, ContentVideo); void audio (boost::weak_ptr, AudioStreamPtr, ContentAudio); void bitmap_text_start (boost::weak_ptr, boost::weak_ptr, ContentBitmapText); void plain_text_start (boost::weak_ptr, boost::weak_ptr, ContentStringText); void subtitle_stop (boost::weak_ptr, boost::weak_ptr, dcpomatic::ContentTime); + void atmos (boost::weak_ptr, ContentAtmos); + dcpomatic::DCPTime one_video_frame () const; void fill_audio (dcpomatic::DCPTimePeriod period); std::pair, dcpomatic::DCPTime> discard_audio ( diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc index 3697fc7b1..133bc0adf 100644 --- a/src/lib/reel_writer.cc +++ b/src/lib/reel_writer.cc @@ -30,11 +30,14 @@ #include "config.h" #include "audio_buffers.h" #include "image.h" +#include +#include #include #include #include #include #include +#include #include #include #include @@ -287,6 +290,23 @@ ReelWriter::write (optional encoded, Frame frame, Eyes eyes) _last_written[eyes] = encoded; } + +void +ReelWriter::write (shared_ptr atmos, AtmosMetadata metadata) +{ + if (!_atmos_asset) { + _atmos_asset = metadata.create (dcp::Fraction(_film->video_frame_rate(), 1)); + if (_film->encrypted()) { + _atmos_asset->set_key(_film->key()); + } + _atmos_asset_writer = _atmos_asset->start_write ( + _film->directory().get() / atmos_asset_filename (_atmos_asset, _reel_index, _reel_count, _content_summary) + ); + } + _atmos_asset_writer->write (atmos); +} + + void ReelWriter::fake_write (int size) { @@ -381,6 +401,24 @@ ReelWriter::finish () _sound_asset->set_file (audio_to); } + + if (_atmos_asset) { + _atmos_asset_writer->finalize (); + boost::filesystem::path atmos_to; + atmos_to /= _film->dir (_film->dcp_name()); + string const aaf = atmos_asset_filename (_atmos_asset, _reel_index, _reel_count, _content_summary); + atmos_to /= aaf; + + boost::system::error_code ec; + boost::filesystem::rename (_film->file(aaf), atmos_to, ec); + if (ec) { + throw FileError ( + String::compose (_("could not move atmos asset into the DCP (%1)"), ec.value ()), aaf + ); + } + + _atmos_asset->set_file (atmos_to); + } } template @@ -590,6 +628,10 @@ ReelWriter::create_reel (list const & refs, listadd (ma); } + if (_atmos_asset) { + reel->add (shared_ptr(new dcp::ReelAtmosAsset(_atmos_asset, 0))); + } + return reel; } @@ -603,6 +645,10 @@ ReelWriter::calculate_digests (boost::function set_progress) if (_sound_asset) { _sound_asset->hash (set_progress); } + + if (_atmos_asset) { + _atmos_asset->hash (set_progress); + } } Frame diff --git a/src/lib/reel_writer.h b/src/lib/reel_writer.h index 0b5a3dad5..17bfc7ba2 100644 --- a/src/lib/reel_writer.h +++ b/src/lib/reel_writer.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2019 Carl Hetherington + Copyright (C) 2012-2020 Carl Hetherington This file is part of DCP-o-matic. @@ -18,12 +18,14 @@ */ +#include "atmos_metadata.h" #include "types.h" #include "dcpomatic_time.h" #include "referenced_reel_asset.h" #include "player_text.h" #include "dcp_text_track.h" #include +#include #include #include @@ -47,6 +49,7 @@ namespace dcp { class SoundAsset; class SoundAssetWriter; class SubtitleAsset; + class AtmosAsset; class ReelAsset; class Reel; } @@ -68,6 +71,7 @@ public: void repeat_write (Frame frame, Eyes eyes); void write (boost::shared_ptr audio); void write (PlayerText text, TextType type, boost::optional track, dcpomatic::DCPTimePeriod period); + void write (boost::shared_ptr atmos, AtmosMetadata metadata); void finish (); boost::shared_ptr create_reel (std::list const & refs, std::list > const & fonts); @@ -115,6 +119,8 @@ private: boost::shared_ptr _sound_asset_writer; boost::shared_ptr _subtitle_asset; std::map > _closed_caption_assets; + boost::shared_ptr _atmos_asset; + boost::shared_ptr _atmos_asset_writer; static int const _info_size; }; diff --git a/src/lib/util.cc b/src/lib/util.cc index f9877523a..74951fc64 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -52,6 +52,7 @@ #include #include #include +#include extern "C" { #include #include @@ -727,6 +728,21 @@ audio_asset_filename (shared_ptr asset, int reel_index, int ree return Config::instance()->dcp_asset_filename_format().get(values, "_" + asset->id() + ".mxf"); } + +string +atmos_asset_filename (shared_ptr asset, int reel_index, int reel_count, optional summary) +{ + dcp::NameFormat::Map values; + values['t'] = "atmos"; + values['r'] = raw_convert (reel_index + 1); + values['n'] = raw_convert (reel_count); + if (summary) { + values['c'] = careful_string_filter (summary.get()); + } + return Config::instance()->dcp_asset_filename_format().get(values, "_" + asset->id() + ".mxf"); +} + + float relaxed_string_to_float (string s) { diff --git a/src/lib/util.h b/src/lib/util.h index 14228ff7f..6cfe37beb 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -28,6 +28,7 @@ #include "types.h" #include "dcpomatic_time.h" #include "audio_mapping.h" +#include #include #include #include @@ -101,6 +102,7 @@ extern void set_backtrace_file (boost::filesystem::path); extern std::map split_get_request (std::string url); extern std::string video_asset_filename (boost::shared_ptr asset, int reel_index, int reel_count, boost::optional content_summary); extern std::string audio_asset_filename (boost::shared_ptr asset, int reel_index, int reel_count, boost::optional content_summary); +extern std::string atmos_asset_filename (boost::shared_ptr asset, int reel_index, int reel_count, boost::optional content_summary); extern float relaxed_string_to_float (std::string); extern std::string careful_string_filter (std::string); extern std::pair audio_channel_types (std::list mapped, int channels); diff --git a/src/lib/video_mxf_decoder.cc b/src/lib/video_mxf_decoder.cc index 030db64ef..488f83778 100644 --- a/src/lib/video_mxf_decoder.cc +++ b/src/lib/video_mxf_decoder.cc @@ -76,7 +76,6 @@ VideoMXFDecoder::pass () double const vfr = _content->active_video_frame_rate (film()); int64_t const frame = _next.frames_round (vfr); - if (frame >= _content->video->length()) { return true; } diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 8682437b3..7d1088276 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -97,6 +97,7 @@ Writer::Writer (shared_ptr film, weak_ptr j) BOOST_FOREACH (DCPTextTrack i, _film->closed_caption_tracks()) { _caption_reels[i] = _reels.begin (); } + _atmos_reel = _reels.begin (); /* Check that the signer is OK */ string reason; @@ -303,6 +304,19 @@ Writer::write (shared_ptr audio, DCPTime const time) } +void +Writer::write (shared_ptr atmos, DCPTime time, AtmosMetadata metadata) +{ + if (_atmos_reel->period().to == time) { + ++_atmos_reel; + DCPOMATIC_ASSERT (_atmos_reel != _reels.end()); + } + + /* We assume that we get a video frame's worth of data here */ + _atmos_reel->write (atmos, metadata); +} + + /** Caller must hold a lock on _state_mutex */ bool Writer::have_sequenced_image_at_queue_head () diff --git a/src/lib/writer.h b/src/lib/writer.h index d09b06264..71e04df96 100644 --- a/src/lib/writer.h +++ b/src/lib/writer.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2019 Carl Hetherington + Copyright (C) 2012-2020 Carl Hetherington This file is part of DCP-o-matic. @@ -22,10 +22,12 @@ * @brief Writer class. */ +#include "atmos_metadata.h" #include "types.h" #include "player_text.h" #include "exception_store.h" #include "dcp_text_track.h" +#include #include #include #include @@ -111,6 +113,7 @@ public: void write (PlayerText text, TextType type, boost::optional, dcpomatic::DCPTimePeriod period); void write (std::list > fonts); void write (ReferencedReelAsset asset); + void write (boost::shared_ptr atmos, dcpomatic::DCPTime time, AtmosMetadata metadata); void finish (); void set_encoder_threads (int threads); @@ -130,6 +133,7 @@ private: std::vector::iterator _audio_reel; std::vector::iterator _subtitle_reel; std::map::iterator> _caption_reels; + std::vector::iterator _atmos_reel; /** our thread */ boost::thread _thread; diff --git a/src/lib/wscript b/src/lib/wscript index ad4dbd87d..c2dfb55d8 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -25,7 +25,11 @@ sources = """ analyse_audio_job.cc analyse_subtitles_job.cc analytics.cc + atmos_content.cc atmos_mxf_content.cc + atmos_decoder.cc + atmos_metadata.cc + atmos_mxf_decoder.cc audio_analysis.cc audio_buffers.cc audio_content.cc diff --git a/src/wx/dcp_panel.cc b/src/wx/dcp_panel.cc index b3eac1268..cb6bf4f24 100644 --- a/src/wx/dcp_panel.cc +++ b/src/wx/dcp_panel.cc @@ -594,13 +594,19 @@ DCPPanel::setup_sensitivity () _reel_length->Enable (_generally_sensitive && _film && _film->reel_type() == REELTYPE_BY_LENGTH); _markers->Enable (_generally_sensitive && _film && !_film->interop()); _metadata->Enable (_generally_sensitive); - _frame_rate_choice->Enable (_generally_sensitive && _film && !_film->references_dcp_video()); - _frame_rate_spin->Enable (_generally_sensitive && _film && !_film->references_dcp_video()); + _frame_rate_choice->Enable (_generally_sensitive && _film && !_film->references_dcp_video() && !_film->contains_atmos_content()); + _frame_rate_spin->Enable (_generally_sensitive && _film && !_film->references_dcp_video() && !_film->contains_atmos_content()); _audio_channels->Enable (_generally_sensitive && _film && !_film->references_dcp_audio()); _audio_processor->Enable (_generally_sensitive && _film && !_film->references_dcp_audio()); _j2k_bandwidth->Enable (_generally_sensitive && _film && !_film->references_dcp_video()); _container->Enable (_generally_sensitive && _film && !_film->references_dcp_video()); - _best_frame_rate->Enable (_generally_sensitive && _film && _film->best_video_frame_rate () != _film->video_frame_rate ()); + _best_frame_rate->Enable ( + _generally_sensitive && + _film && + _film->best_video_frame_rate () != _film->video_frame_rate() && + !_film->references_dcp_video() && + !_film->contains_atmos_content() + ); _resolution->Enable (_generally_sensitive && _film && !_film->references_dcp_video()); _three_d->Enable (_generally_sensitive && _film && !_film->references_dcp_video()); _standard->Enable (_generally_sensitive && _film && !_film->references_dcp_video() && !_film->references_dcp_audio()); diff --git a/test/atmos_test.cc b/test/atmos_test.cc new file mode 100644 index 000000000..019e2d2fb --- /dev/null +++ b/test/atmos_test.cc @@ -0,0 +1,91 @@ +/* + Copyright (C) 2020 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/config.h" +#include "lib/content.h" +#include "lib/content_factory.h" +#include "lib/dcp_content.h" +#include "lib/film.h" +#include "test.h" +#include +#include + + +using std::string; +using std::vector; +using boost::optional; +using boost::shared_ptr; + + +BOOST_AUTO_TEST_CASE (atmos_passthrough_test) +{ + shared_ptr film = new_test_film2 ("atmos_passthrough_test"); + boost::filesystem::path ref = TestPaths::private_data / "atmos_asset.mxf"; + shared_ptr content = content_factory (TestPaths::private_data / "atmos_asset.mxf").front(); + film->examine_and_add_content (content); + BOOST_REQUIRE (!wait_for_jobs()); + + film->make_dcp (); + BOOST_REQUIRE (!wait_for_jobs()); + + BOOST_REQUIRE (mxf_atmos_files_same(ref, dcp_file(film, "atmos"), true)); +} + + +BOOST_AUTO_TEST_CASE (atmos_encrypted_passthrough_test) +{ + shared_ptr film = new_test_film2 ("atmos_encrypted_passthrough_test"); + boost::filesystem::path ref = TestPaths::private_data / "atmos_asset.mxf"; + shared_ptr content = content_factory (TestPaths::private_data / "atmos_asset.mxf").front(); + film->examine_and_add_content (content); + BOOST_REQUIRE (!wait_for_jobs()); + + film->set_encrypted (true); + film->_key = dcp::Key ("4fac12927eb122af1c2781aa91f3a4cc"); + film->make_dcp (); + BOOST_REQUIRE (!wait_for_jobs()); + + BOOST_REQUIRE (!mxf_atmos_files_same(ref, dcp_file(film, "atmos"))); + + dcp::EncryptedKDM kdm = film->make_kdm ( + Config::instance()->decryption_chain()->leaf(), + vector(), + dcp_file(film, "cpl"), + dcp::LocalTime(), + dcp::LocalTime(), + dcp::MODIFIED_TRANSITIONAL_1, + false, + optional() + ); + + shared_ptr film2 = new_test_film2 ("atmos_encrypted_passthrough_test2"); + shared_ptr content2 (new DCPContent(film->dir(film->dcp_name()))); + content2->add_kdm (kdm); + film2->examine_and_add_content (content2); + BOOST_REQUIRE (!wait_for_jobs()); + + std::cout << "making 2nd dcp.\n"; + film2->make_dcp (); + BOOST_REQUIRE (!wait_for_jobs()); + + BOOST_CHECK (mxf_atmos_files_same(ref, dcp_file(film2, "atmos"), true)); +} + diff --git a/test/test.cc b/test/test.cc index 90decc2df..32d32f988 100644 --- a/test/test.cc +++ b/test/test.cc @@ -237,6 +237,83 @@ check_mxf_audio_file (boost::filesystem::path ref, boost::filesystem::path check } } + +/** @return true if the files are the same, otherwise false */ +bool +mxf_atmos_files_same (boost::filesystem::path ref, boost::filesystem::path check, bool verbose) +{ + ASDCP::ATMOS::MXFReader ref_reader; + BOOST_REQUIRE (!ASDCP_FAILURE(ref_reader.OpenRead(ref.string().c_str()))); + + ASDCP::ATMOS::AtmosDescriptor ref_desc; + BOOST_REQUIRE (!ASDCP_FAILURE(ref_reader.FillAtmosDescriptor(ref_desc))); + + ASDCP::ATMOS::MXFReader check_reader; + BOOST_REQUIRE (!ASDCP_FAILURE(check_reader.OpenRead(check.string().c_str()))); + + ASDCP::ATMOS::AtmosDescriptor check_desc; + BOOST_REQUIRE (!ASDCP_FAILURE(check_reader.FillAtmosDescriptor(check_desc))); + + if (ref_desc.EditRate.Numerator != check_desc.EditRate.Numerator) { + if (verbose) { + std::cout << "EditRate.Numerator differs.\n"; + } + return false; + } + if (ref_desc.EditRate.Denominator != check_desc.EditRate.Denominator) { + if (verbose) { + std::cout << "EditRate.Denominator differs.\n"; + } + return false; + } + if (ref_desc.ContainerDuration != check_desc.ContainerDuration) { + if (verbose) { + std::cout << "EditRate.ContainerDuration differs.\n"; + } + return false; + } + if (ref_desc.FirstFrame != check_desc.FirstFrame) { + if (verbose) { + std::cout << "EditRate.FirstFrame differs.\n"; + } + return false; + } + if (ref_desc.MaxChannelCount != check_desc.MaxChannelCount) { + if (verbose) { + std::cout << "EditRate.MaxChannelCount differs.\n"; + } + return false; + } + if (ref_desc.MaxObjectCount != check_desc.MaxObjectCount) { + if (verbose) { + std::cout << "EditRate.MaxObjectCount differs.\n"; + } + return false; + } + if (ref_desc.AtmosVersion != check_desc.AtmosVersion) { + if (verbose) { + std::cout << "EditRate.AtmosVersion differs.\n"; + } + return false; + } + + ASDCP::DCData::FrameBuffer ref_buffer (Kumu::Megabyte); + ASDCP::DCData::FrameBuffer check_buffer (Kumu::Megabyte); + for (size_t i = 0; i < ref_desc.ContainerDuration; ++i) { + ref_reader.ReadFrame (i, ref_buffer, 0); + check_reader.ReadFrame (i, check_buffer, 0); + if (memcmp(ref_buffer.RoData(), check_buffer.RoData(), ref_buffer.Size())) { + if (verbose) { + std::cout << "data differs.\n"; + } + return false; + } + } + + return true; +} + + void check_image (boost::filesystem::path ref, boost::filesystem::path check, double threshold) { diff --git a/test/test.h b/test/test.h index dd919b408..77108af46 100644 --- a/test/test.h +++ b/test/test.h @@ -43,6 +43,7 @@ extern void check_dcp (boost::filesystem::path, boost::shared_ptr); extern void check_file (boost::filesystem::path ref, boost::filesystem::path check); extern void check_wav_file (boost::filesystem::path ref, boost::filesystem::path check); extern void check_mxf_audio_file (boost::filesystem::path ref, boost::filesystem::path check); +extern bool mxf_atmos_files_same (boost::filesystem::path ref, boost::filesystem::path check, bool verbose = false); extern void check_xml (boost::filesystem::path, boost::filesystem::path, std::list); extern void check_file (boost::filesystem::path, boost::filesystem::path); extern void check_ffmpeg (boost::filesystem::path, boost::filesystem::path, int audio_tolerance); diff --git a/test/wscript b/test/wscript index 9c67eacc4..2ac62f633 100644 --- a/test/wscript +++ b/test/wscript @@ -45,6 +45,7 @@ def build(bld): obj.use = 'libdcpomatic2' obj.source = """ 4k_test.cc + atmos_test.cc audio_analysis_test.cc audio_buffers_test.cc audio_delay_test.cc -- 2.30.2