Make Atmos content work more like other content. Now its MXFs v2.15.82
authorCarl Hetherington <cth@carlh.net>
Fri, 19 Jun 2020 11:30:02 +0000 (13:30 +0200)
committerCarl Hetherington <cth@carlh.net>
Fri, 19 Jun 2020 13:07:30 +0000 (15:07 +0200)
are re-written, meaning that they can be encrypted.

This (along with the libdcp update) also fixes assorted Atmos bugs.

39 files changed:
cscript
src/lib/atmos_content.cc [new file with mode: 0644]
src/lib/atmos_content.h [new file with mode: 0644]
src/lib/atmos_decoder.cc [new file with mode: 0644]
src/lib/atmos_decoder.h [new file with mode: 0644]
src/lib/atmos_metadata.cc [new file with mode: 0644]
src/lib/atmos_metadata.h [new file with mode: 0644]
src/lib/atmos_mxf_content.cc
src/lib/atmos_mxf_content.h
src/lib/atmos_mxf_decoder.cc [new file with mode: 0644]
src/lib/atmos_mxf_decoder.h [new file with mode: 0644]
src/lib/content.h
src/lib/content_atmos.h [new file with mode: 0644]
src/lib/dcp_content.cc
src/lib/dcp_decoder.cc
src/lib/dcp_decoder.h
src/lib/dcp_encoder.cc
src/lib/dcp_encoder.h
src/lib/dcp_examiner.cc
src/lib/dcp_examiner.h
src/lib/decoder.h
src/lib/decoder_factory.cc
src/lib/film.cc
src/lib/film.h
src/lib/player.cc
src/lib/player.h
src/lib/reel_writer.cc
src/lib/reel_writer.h
src/lib/util.cc
src/lib/util.h
src/lib/video_mxf_decoder.cc
src/lib/writer.cc
src/lib/writer.h
src/lib/wscript
src/wx/dcp_panel.cc
test/atmos_test.cc [new file with mode: 0644]
test/test.cc
test/test.h
test/wscript

diff --git a/cscript b/cscript
index 6966864a66ffcb3d691ae16b085d7278fd251bd3..864ec90c2d71db1a3f2c83eaab45419a3d0821ea 100644 (file)
--- 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 (file)
index 0000000..c424f96
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+    Copyright (C) 2020 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/>.
+
+*/
+
+
+#include "atmos_content.h"
+#include <dcp/raw_convert.h>
+#include <libxml++/libxml++.h>
+
+
+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<Frame>("Length");
+       _edit_rate = dcp::Fraction (node->string_child("EditRate"));
+}
+
+
+shared_ptr<AtmosContent>
+AtmosContent::from_xml (Content* parent, cxml::ConstNodePtr node)
+{
+       return shared_ptr<AtmosContent> (new AtmosContent(parent, node));
+}
+
+
+void
+AtmosContent::as_xml (xmlpp::Node* node) const
+{
+       node->add_child("Length")->add_child_text(dcp::raw_convert<string>(_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 (file)
index 0000000..0344b62
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+    Copyright (C) 2020 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/>.
+
+*/
+
+
+#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<AtmosContent> 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 (file)
index 0000000..4ebb07f
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+    Copyright (C) 2020 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/>.
+
+*/
+
+
+#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<const Content> content)
+       : DecoderPart (parent)
+       , _content (content)
+{
+
+}
+
+
+void
+AtmosDecoder::seek ()
+{
+       _position = boost::none;
+}
+
+
+void
+AtmosDecoder::emit (shared_ptr<const Film> film, shared_ptr<const dcp::AtmosFrame> 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 (file)
index 0000000..3003d5a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    Copyright (C) 2020 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/>.
+
+*/
+
+
+#include "atmos_metadata.h"
+#include "content_atmos.h"
+#include "decoder_part.h"
+#include <boost/signals2.hpp>
+
+
+class AtmosDecoder : public DecoderPart
+{
+public:
+       AtmosDecoder (Decoder* parent, boost::shared_ptr<const Content> content);
+
+       boost::optional<dcpomatic::ContentTime> position (boost::shared_ptr<const Film>) const {
+               return _position;
+       }
+
+       void seek ();
+
+       void emit (boost::shared_ptr<const Film> film, boost::shared_ptr<const dcp::AtmosFrame> data, Frame frame, AtmosMetadata metadata);
+
+       boost::signals2::signal<void (ContentAtmos)> Data;
+
+private:
+       boost::shared_ptr<const Content> _content;
+       boost::optional<dcpomatic::ContentTime> _position;
+};
diff --git a/src/lib/atmos_metadata.cc b/src/lib/atmos_metadata.cc
new file mode 100644 (file)
index 0000000..0a4b4f1
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+    Copyright (C) 2012-2020 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/>.
+
+*/
+
+
+#include "atmos_metadata.h"
+#include <dcp/atmos_asset.h>
+
+
+using boost::shared_ptr;
+
+
+AtmosMetadata::AtmosMetadata (shared_ptr<const dcp::AtmosAsset> 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<dcp::AtmosAsset>
+AtmosMetadata::create (dcp::Fraction edit_rate) const
+{
+       return shared_ptr<dcp::AtmosAsset> (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 (file)
index 0000000..4e3fc85
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+    Copyright (C) 2012-2020 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/>.
+
+*/
+
+#ifndef DCPOMATIC_ATMOS_METADATA_H
+#define DCPOMATIC_ATMOS_METADATA_H
+
+#include <dcp/atmos_asset.h>
+#include <boost/shared_ptr.hpp>
+
+class AtmosMetadata
+{
+public:
+       AtmosMetadata (boost::shared_ptr<const dcp::AtmosAsset> asset);
+       boost::shared_ptr<dcp::AtmosAsset> create (dcp::Fraction edit_rate) const;
+
+private:
+       int _first_frame;
+       int _max_channel_count;
+       int _max_object_count;
+       int _atmos_version;
+};
+
+#endif
+
index 4c1201c70fa649d913bdecc61343cd38e51edf17..36ae8871a774183f57b5b0e67e2b3451e1f5cc82 100644 (file)
@@ -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<Frame>("Length").get_value_or(0);
+       atmos = AtmosContent::from_xml (this, node);
 }
 
 bool
@@ -78,7 +76,9 @@ AtmosMXFContent::examine (shared_ptr<const Film> film, shared_ptr<Job> 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<string>(_length));
+       atmos->as_xml (node);
 }
 
 DCPTime
 AtmosMXFContent::full_length (shared_ptr<const Film> 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);
 }
index 06e9e747d9cb61759eba3268d6dd32d1803f8555..350c4a35d3b48a8f8270847a5b3df5de39f42d2a 100644 (file)
@@ -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 (file)
index 0000000..3f6f4a8
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+    Copyright (C) 2020 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/>.
+
+*/
+
+#include "atmos_content.h"
+#include "atmos_decoder.h"
+#include "atmos_mxf_content.h"
+#include "atmos_mxf_decoder.h"
+#include "dcpomatic_time.h"
+#include <dcp/atmos_asset.h>
+#include <dcp/atmos_asset_reader.h>
+
+using boost::shared_ptr;
+
+AtmosMXFDecoder::AtmosMXFDecoder (boost::shared_ptr<const Film> film, boost::shared_ptr<const AtmosMXFContent> content)
+       : Decoder (film)
+       , _content (content)
+{
+       atmos.reset (new AtmosDecoder(this, content));
+
+       shared_ptr<dcp::AtmosAsset> 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 (file)
index 0000000..f5b7fe1
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+    Copyright (C) 2020 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/>.
+
+*/
+
+#include "atmos_metadata.h"
+#include "dcpomatic_time.h"
+#include "decoder.h"
+#include <dcp/atmos_asset_reader.h>
+
+class AtmosMXFContent;
+
+class AtmosMXFDecoder : public Decoder
+{
+public:
+       AtmosMXFDecoder (boost::shared_ptr<const Film> film, boost::shared_ptr<const AtmosMXFContent>);
+
+       bool pass ();
+       void seek (dcpomatic::ContentTime t, bool accurate);
+
+private:
+       boost::shared_ptr<const AtmosMXFContent> _content;
+       dcpomatic::ContentTime _next;
+       boost::shared_ptr<dcp::AtmosAssetReader> _reader;
+       boost::optional<AtmosMetadata> _metadata;
+};
+
index a2d78aa68539423cca457abb2005f558e6ca68fa..5f8e9f53d3a8a0a25c79315635f4bd7653a01f0f 100644 (file)
@@ -46,6 +46,7 @@ namespace cxml {
 
 class Job;
 class Film;
+class AtmosContent;
 
 class ContentProperty
 {
@@ -190,6 +191,7 @@ public:
        boost::shared_ptr<VideoContent> video;
        boost::shared_ptr<AudioContent> audio;
        std::list<boost::shared_ptr<TextContent> > text;
+       boost::shared_ptr<AtmosContent> atmos;
 
        boost::shared_ptr<TextContent> only_text () const;
        boost::shared_ptr<TextContent> 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 (file)
index 0000000..57b7e40
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    Copyright (C) 2020 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/>.
+
+*/
+
+#ifndef DCPOMATIC_CONTENT_ATMOS_H
+#define DCPOMATIC_CONTENT_ATMOS_H
+
+#include "atmos_metadata.h"
+#include "types.h"
+#include <dcp/atmos_asset_reader.h>
+#include <boost/shared_ptr.hpp>
+
+/** @class ContentAtmos
+ *  @brief Some Atmos data that has come out of a decoder.
+ */
+class ContentAtmos
+{
+public:
+       ContentAtmos (boost::shared_ptr<const dcp::AtmosFrame> data_, Frame frame_, AtmosMetadata metadata_)
+               : data (data_)
+               , frame (frame_)
+               , metadata (metadata_)
+       {}
+
+       boost::shared_ptr<const dcp::AtmosFrame> data;
+       Frame frame;
+       AtmosMetadata metadata;
+};
+
+#endif
index ca210b5587ce45cbdd4eee40037649843246f41a..d2c1df748ddc5d3dbf529a0f3ea4f7147f4d42a1 100644 (file)
@@ -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<const Film> film, shared_ptr<Job> 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);
index ab724cd6c75bbb7a5f02326e15b92f3a964a0322..30e3372522e50c4e73e417add515a143232396ff 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2014-2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014-2020 Carl Hetherington <cth@carlh.net>
 
     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 <dcp/sound_asset_reader.h>
 #include <dcp/subtitle_image.h>
 #include <dcp/decrypted_kdm.h>
+#include <dcp/reel_atmos_asset.h>
 #include <boost/foreach.hpp>
 #include <iostream>
 
@@ -76,6 +78,9 @@ DCPDecoder::DCPDecoder (shared_ptr<const Film> film, shared_ptr<const DCPContent
                        /* XXX: this time here should be the time of the first subtitle, not 0 */
                        text.push_back (shared_ptr<TextDecoder> (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<dcp::AtmosAsset> asset = (*_reel)->atmos()->asset();
+               _atmos_reader = asset->start_read();
+               _atmos_metadata = AtmosMetadata (asset);
+       } else {
+               _atmos_reader.reset ();
+               _atmos_metadata = boost::none;
+       }
 }
 
 void
index 4de8c86299a0af3fc096387f6f3ea3bab3a77033..1abe39a27d7e56bf74bb8a58be4ec557f7b7d301 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2014-2016 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014-2020 Carl Hetherington <cth@carlh.net>
 
     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 <dcp/mono_picture_asset_reader.h>
@@ -93,6 +94,8 @@ private:
        boost::shared_ptr<dcp::StereoPictureAssetReader> _stereo_reader;
        /** Reader for current sound asset, if applicable */
        boost::shared_ptr<dcp::SoundAssetReader> _sound_reader;
+       boost::shared_ptr<dcp::AtmosAssetReader> _atmos_reader;
+       boost::optional<AtmosMetadata> _atmos_metadata;
 
        bool _decode_referenced;
        boost::optional<int> _forced_reduction;
index d17c6c985b07bd1f5529e75adddae38c78b0fe45..438a73fd64d4b95ea93facb66091a2f7d0b84d30 100644 (file)
@@ -64,6 +64,7 @@ DCPEncoder::DCPEncoder (shared_ptr<const Film> film, weak_ptr<Job> 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<const Content> c, film->content ()) {
                BOOST_FOREACH (shared_ptr<TextContent> 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<DCPTextTrack> track,
        }
 }
 
+
+void
+DCPEncoder::atmos (shared_ptr<const dcp::AtmosFrame> data, DCPTime time, AtmosMetadata metadata)
+{
+       _writer->write (data, time, metadata);
+}
+
+
 optional<float>
 DCPEncoder::current_rate () const
 {
index cc2de3e0c924bb1881aa3eef57907ba86de793a6..e5df30fb524ac9cb897883bdfe1addb610f65310 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
+#include "atmos_metadata.h"
 #include "types.h"
 #include "player_text.h"
 #include "dcp_text_track.h"
 #include "encoder.h"
+#include <dcp/atmos_frame.h>
 #include <boost/weak_ptr.hpp>
 
 class Film;
@@ -54,6 +56,7 @@ private:
        void video (boost::shared_ptr<PlayerVideo>, dcpomatic::DCPTime);
        void audio (boost::shared_ptr<AudioBuffers>, dcpomatic::DCPTime);
        void text (PlayerText, TextType, boost::optional<DCPTextTrack>, dcpomatic::DCPTimePeriod);
+       void atmos (boost::shared_ptr<const dcp::AtmosFrame>, dcpomatic::DCPTime, AtmosMetadata metadata);
 
        boost::shared_ptr<Writer> _writer;
        boost::shared_ptr<J2KEncoder> _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;
 };
index d04dacdd6ca573ec7afbf8975b020f2c8d6277ae..133c1614e922b5b5f8b6efbcb8a88d7321c2d785 100644 (file)
@@ -39,6 +39,7 @@
 #include <dcp/sound_asset.h>
 #include <dcp/sound_asset_reader.h>
 #include <dcp/subtitle_asset.h>
+#include <dcp/reel_atmos_asset.h>
 #include <dcp/reel_subtitle_asset.h>
 #include <dcp/reel_closed_caption_asset.h>
 #include <dcp/reel_markers_asset.h>
@@ -65,6 +66,7 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content, bool tolerant)
        , _needs_assets (false)
        , _kdm_valid (false)
        , _three_d (false)
+       , _has_atmos (false)
 {
        shared_ptr<dcp::CPL> cpl;
 
@@ -96,6 +98,9 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> 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<const DCPContent> 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<const DCPContent> 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<const DCPContent> 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;
index 21aa8d3ab58537de5c4446f899f94d4bcc4bb459..b4c87596c70e354079937132d289a06ed2ba8a42 100644 (file)
@@ -131,6 +131,10 @@ public:
                return _content_version;
        }
 
+       bool has_atmos () const {
+               return _has_atmos;
+       }
+
 private:
        boost::optional<double> _video_frame_rate;
        boost::optional<dcp::Size> _video_size;
@@ -156,4 +160,5 @@ private:
        std::map<dcp::Marker, dcp::Time> _markers;
        std::vector<dcp::Rating> _ratings;
        std::string _content_version;
+       bool _has_atmos;
 };
index ddea58aacbfc636c9c29d0264300155046c5378b..316109ebb95e5eed3d12d3127c75354e3af33f7d 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
 
     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<VideoDecoder> video;
        boost::shared_ptr<AudioDecoder> audio;
        std::list<boost::shared_ptr<TextDecoder> > text;
+       boost::shared_ptr<AtmosDecoder> atmos;
 
        boost::shared_ptr<TextDecoder> only_text () const;
 
index 2ded95fefe1b343609f62959dd29b3c7a1edfa08..12e3f3ddd46fe7356b7a336ac3a0bc95e0575eb6 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2016-2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2016-2020 Carl Hetherington <cth@carlh.net>
 
     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<const Film> film, shared_ptr<const Content> content,
                return shared_ptr<Decoder> (new VideoMXFDecoder(film, vmc));
        }
 
+       shared_ptr<const AtmosMXFContent> amc = dynamic_pointer_cast<const AtmosMXFContent> (content);
+       if (amc) {
+               return shared_ptr<Decoder> (new AtmosMXFDecoder(film, amc));
+       }
+
        return shared_ptr<Decoder> ();
 }
index c04f2996f255fd4dabd0087012efb02d1e44a036..dafe158b050f9618e5c935dd07126dee51643586 100644 (file)
@@ -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<int> atmos_rate;
+       BOOST_FOREACH (shared_ptr<Content> 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<Content> i, content()) {
                shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent>(i);
@@ -1779,6 +1795,18 @@ Film::references_dcp_audio () const
        return false;
 }
 
+
+bool
+Film::contains_atmos_content () const
+{
+       BOOST_FOREACH (shared_ptr<Content> i, _playlist->content()) {
+               if (i->atmos) {
+                       return true;
+               }
+       }
+}
+
+
 list<DCPTextTrack>
 Film::closed_caption_tracks () const
 {
index 64bb159c7ba7a2892c5d07d07b34cdc6e31c81f7..f5c20bccdbec79048285b82ab5ce4b87e802bfcd 100644 (file)
@@ -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 <typename> friend class ChangeSignaller;
 
        boost::filesystem::path info_file (dcpomatic::DCPTimePeriod p) const;
index fc821d6c70e55f10a697fb23861e0fcffff9ff2b..b45401fa90cf8ca0b3b831b6f8607cc23ae535ae 100644 (file)
@@ -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 = 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>(piece), _1));
+               }
        }
 
        _stream_states.clear ();
@@ -1270,3 +1272,10 @@ Player::playlist () const
        return _playlist ? _playlist : _film->playlist();
 }
 
+
+void
+Player::atmos (weak_ptr<Piece>, ContentAtmos data)
+{
+       Atmos (data.data, DCPTime::from_frames(data.frame, _film->video_frame_rate()), data.metadata);
+}
+
index bb2a0c613e3760c5cce9fdb6642d7abb535e94c2..ea81ae939bb85e181e557502e1258921543f8849 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2020 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 #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<void (PlayerText, TextType, boost::optional<DCPTextTrack>, dcpomatic::DCPTimePeriod)> Text;
+       boost::signals2::signal<void (boost::shared_ptr<const dcp::AtmosFrame>, dcpomatic::DCPTime, AtmosMetadata)> Atmos;
 
 private:
        friend class PlayerWrapper;
@@ -129,11 +133,14 @@ private:
        dcpomatic::ContentTime dcp_to_content_time (boost::shared_ptr<const Piece> piece, dcpomatic::DCPTime t) const;
        dcpomatic::DCPTime content_time_to_dcp (boost::shared_ptr<const Piece> piece, dcpomatic::ContentTime t) const;
        boost::shared_ptr<PlayerVideo> black_player_video_frame (Eyes eyes) const;
+
        void video (boost::weak_ptr<Piece>, ContentVideo);
        void audio (boost::weak_ptr<Piece>, AudioStreamPtr, ContentAudio);
        void bitmap_text_start (boost::weak_ptr<Piece>, boost::weak_ptr<const TextContent>, ContentBitmapText);
        void plain_text_start (boost::weak_ptr<Piece>, boost::weak_ptr<const TextContent>, ContentStringText);
        void subtitle_stop (boost::weak_ptr<Piece>, boost::weak_ptr<const TextContent>, dcpomatic::ContentTime);
+       void atmos (boost::weak_ptr<Piece>, ContentAtmos);
+
        dcpomatic::DCPTime one_video_frame () const;
        void fill_audio (dcpomatic::DCPTimePeriod period);
        std::pair<boost::shared_ptr<AudioBuffers>, dcpomatic::DCPTime> discard_audio (
index 3697fc7b147472992d1e00470ece76735994f637..133bc0adf2e7b2302f9b1e169b8f30271c44b475 100644 (file)
 #include "config.h"
 #include "audio_buffers.h"
 #include "image.h"
+#include <dcp/atmos_asset.h>
+#include <dcp/atmos_asset_writer.h>
 #include <dcp/mono_picture_asset.h>
 #include <dcp/stereo_picture_asset.h>
 #include <dcp/sound_asset.h>
 #include <dcp/sound_asset_writer.h>
 #include <dcp/reel.h>
+#include <dcp/reel_atmos_asset.h>
 #include <dcp/reel_mono_picture_asset.h>
 #include <dcp/reel_stereo_picture_asset.h>
 #include <dcp/reel_sound_asset.h>
@@ -287,6 +290,23 @@ ReelWriter::write (optional<Data> encoded, Frame frame, Eyes eyes)
        _last_written[eyes] = encoded;
 }
 
+
+void
+ReelWriter::write (shared_ptr<const dcp::AtmosFrame> 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 <class T>
@@ -590,6 +628,10 @@ ReelWriter::create_reel (list<ReferencedReelAsset> const & refs, list<shared_ptr
                reel->add (ma);
        }
 
+       if (_atmos_asset) {
+               reel->add (shared_ptr<dcp::ReelAtmosAsset>(new dcp::ReelAtmosAsset(_atmos_asset, 0)));
+       }
+
        return reel;
 }
 
@@ -603,6 +645,10 @@ ReelWriter::calculate_digests (boost::function<void (float)> set_progress)
        if (_sound_asset) {
                _sound_asset->hash (set_progress);
        }
+
+       if (_atmos_asset) {
+               _atmos_asset->hash (set_progress);
+       }
 }
 
 Frame
index 0b5a3dad59c321101b161fbdfb0eaf01d409dded..17bfc7ba2c191482e674bb0645b470bf965e6e3e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
+#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 <dcp/picture_asset_writer.h>
+#include <dcp/atmos_asset_writer.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/weak_ptr.hpp>
 
@@ -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<const AudioBuffers> audio);
        void write (PlayerText text, TextType type, boost::optional<DCPTextTrack> track, dcpomatic::DCPTimePeriod period);
+       void write (boost::shared_ptr<const dcp::AtmosFrame> atmos, AtmosMetadata metadata);
 
        void finish ();
        boost::shared_ptr<dcp::Reel> create_reel (std::list<ReferencedReelAsset> const & refs, std::list<boost::shared_ptr<dcpomatic::Font> > const & fonts);
@@ -115,6 +119,8 @@ private:
        boost::shared_ptr<dcp::SoundAssetWriter> _sound_asset_writer;
        boost::shared_ptr<dcp::SubtitleAsset> _subtitle_asset;
        std::map<DCPTextTrack, boost::shared_ptr<dcp::SubtitleAsset> > _closed_caption_assets;
+       boost::shared_ptr<dcp::AtmosAsset> _atmos_asset;
+       boost::shared_ptr<dcp::AtmosAssetWriter> _atmos_asset_writer;
 
        static int const _info_size;
 };
index f9877523a0d3b4ef4b0ef56fc1163e866fb5f6fc..74951fc64e1ef9bb076e5959cdd43dddd140e25f 100644 (file)
@@ -52,6 +52,7 @@
 #include <dcp/picture_asset.h>
 #include <dcp/sound_asset.h>
 #include <dcp/subtitle_asset.h>
+#include <dcp/atmos_asset.h>
 extern "C" {
 #include <libavfilter/avfilter.h>
 #include <libavformat/avformat.h>
@@ -727,6 +728,21 @@ audio_asset_filename (shared_ptr<dcp::SoundAsset> asset, int reel_index, int ree
        return Config::instance()->dcp_asset_filename_format().get(values, "_" + asset->id() + ".mxf");
 }
 
+
+string
+atmos_asset_filename (shared_ptr<dcp::AtmosAsset> asset, int reel_index, int reel_count, optional<string> summary)
+{
+       dcp::NameFormat::Map values;
+       values['t'] = "atmos";
+       values['r'] = raw_convert<string> (reel_index + 1);
+       values['n'] = raw_convert<string> (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)
 {
index 14228ff7f2ccbba86494a52291f96ce9f81ce1d5..6cfe37bebd0f6034ebe87f40cde23822426e52fb 100644 (file)
@@ -28,6 +28,7 @@
 #include "types.h"
 #include "dcpomatic_time.h"
 #include "audio_mapping.h"
+#include <dcp/atmos_asset.h>
 #include <dcp/decrypted_kdm.h>
 #include <dcp/util.h>
 #include <dcp/subtitle_image.h>
@@ -101,6 +102,7 @@ extern void set_backtrace_file (boost::filesystem::path);
 extern std::map<std::string, std::string> split_get_request (std::string url);
 extern std::string video_asset_filename (boost::shared_ptr<dcp::PictureAsset> asset, int reel_index, int reel_count, boost::optional<std::string> content_summary);
 extern std::string audio_asset_filename (boost::shared_ptr<dcp::SoundAsset> asset, int reel_index, int reel_count, boost::optional<std::string> content_summary);
+extern std::string atmos_asset_filename (boost::shared_ptr<dcp::AtmosAsset> asset, int reel_index, int reel_count, boost::optional<std::string> content_summary);
 extern float relaxed_string_to_float (std::string);
 extern std::string careful_string_filter (std::string);
 extern std::pair<int, int> audio_channel_types (std::list<int> mapped, int channels);
index 030db64ef6e2fd2300369c7c3ecc996bb0790b58..488f837784b4bb57c12bf5d01255e32f7e5030ee 100644 (file)
@@ -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;
        }
index 8682437b37888682959a96bf0cd286f9d6ac29f9..7d1088276995a242300402684facbf0290e3876f 100644 (file)
@@ -97,6 +97,7 @@ Writer::Writer (shared_ptr<const Film> film, weak_ptr<Job> 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<const AudioBuffers> audio, DCPTime const time)
 }
 
 
+void
+Writer::write (shared_ptr<const dcp::AtmosFrame> 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 ()
index d09b062649f94ea55f74ad563847f54118330772..71e04df96053c7cf9e7b756421b9e658e925a797 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
  *  @brief Writer class.
  */
 
+#include "atmos_metadata.h"
 #include "types.h"
 #include "player_text.h"
 #include "exception_store.h"
 #include "dcp_text_track.h"
+#include <dcp/atmos_frame.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/weak_ptr.hpp>
 #include <boost/thread.hpp>
@@ -111,6 +113,7 @@ public:
        void write (PlayerText text, TextType type, boost::optional<DCPTextTrack>, dcpomatic::DCPTimePeriod period);
        void write (std::list<boost::shared_ptr<dcpomatic::Font> > fonts);
        void write (ReferencedReelAsset asset);
+       void write (boost::shared_ptr<const dcp::AtmosFrame> atmos, dcpomatic::DCPTime time, AtmosMetadata metadata);
        void finish ();
 
        void set_encoder_threads (int threads);
@@ -130,6 +133,7 @@ private:
        std::vector<ReelWriter>::iterator _audio_reel;
        std::vector<ReelWriter>::iterator _subtitle_reel;
        std::map<DCPTextTrack, std::vector<ReelWriter>::iterator> _caption_reels;
+       std::vector<ReelWriter>::iterator _atmos_reel;
 
        /** our thread */
        boost::thread _thread;
index ad4dbd87d4aaf14e40b1e2d79a64db983d8daf1a..c2dfb55d87d32880dabaf5f1a8e36b66e923d88a 100644 (file)
@@ -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
index b3eac1268d25690ee4eb1ee37840814b81bb09c2..cb6bf4f242fcd6de7117f5b8d6f9ffe74f29994e 100644 (file)
@@ -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 (file)
index 0000000..019e2d2
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+    Copyright (C) 2020 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/>.
+
+*/
+
+
+#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 <boost/test/unit_test.hpp>
+#include <iostream>
+
+
+using std::string;
+using std::vector;
+using boost::optional;
+using boost::shared_ptr;
+
+
+BOOST_AUTO_TEST_CASE (atmos_passthrough_test)
+{
+       shared_ptr<Film> film = new_test_film2 ("atmos_passthrough_test");
+       boost::filesystem::path ref = TestPaths::private_data / "atmos_asset.mxf";
+       shared_ptr<Content> 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> film = new_test_film2 ("atmos_encrypted_passthrough_test");
+       boost::filesystem::path ref = TestPaths::private_data / "atmos_asset.mxf";
+       shared_ptr<Content> 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<string>(),
+               dcp_file(film, "cpl"),
+               dcp::LocalTime(),
+               dcp::LocalTime(),
+               dcp::MODIFIED_TRANSITIONAL_1,
+               false,
+               optional<int>()
+               );
+
+       shared_ptr<Film> film2 = new_test_film2 ("atmos_encrypted_passthrough_test2");
+       shared_ptr<DCPContent> 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));
+}
+
index 90decc2df31d677f9f3699478eaf0bc3a43e1ed7..32d32f98879f8ddefcf16f7d19447085d5d873a6 100644 (file)
@@ -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)
 {
index dd919b4087da3bcdaa6a42c9cf05493d11b19c04..77108af46b586f267dc53db0830f671039148a09 100644 (file)
@@ -43,6 +43,7 @@ extern void check_dcp (boost::filesystem::path, boost::shared_ptr<const Film>);
 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<std::string>);
 extern void check_file (boost::filesystem::path, boost::filesystem::path);
 extern void check_ffmpeg (boost::filesystem::path, boost::filesystem::path, int audio_tolerance);
index 9c67eacc4a2711f661b54639eba0184a974f43ae..2ac62f63302d2d20d26adc1fe3a33834080fff3e 100644 (file)
@@ -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