Allow configuration of MXF/XML filenames (part of #710).
authorCarl Hetherington <cth@carlh.net>
Sun, 31 Jul 2016 22:52:02 +0000 (23:52 +0100)
committerCarl Hetherington <cth@carlh.net>
Sun, 31 Jul 2016 22:52:02 +0000 (23:52 +0100)
20 files changed:
ChangeLog
src/lib/dcp_subtitle_decoder.cc
src/lib/dcpomatic_time.h
src/lib/ffmpeg_subtitle_stream.cc
src/lib/film.cc
src/lib/film.h
src/lib/overlaps.cc
src/lib/playlist.cc
src/lib/playlist.h
src/lib/reel_writer.cc
src/lib/reel_writer.h
src/lib/subtitle_decoder.cc
src/lib/text_subtitle_decoder.cc
src/lib/util.cc
src/lib/util.h
src/lib/writer.cc
src/wx/timeline.cc
test/dcpomatic_time_test.cc
test/file_naming_test.cc [new file with mode: 0644]
test/wscript

index 63d924e08a1e2dc143a0577cb75c0a2d39bca9d2..25d5b00dddee5c46c20546a3df37354175656d80 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2016-07-31  Carl Hetherington  <cth@carlh.net>
+
+       * Allow configuration of MXF/XML filenames (part of #710).
+
 2016-07-29  Carl Hetherington  <cth@carlh.net>
 
        * Version 2.9.8 released.
index 81dfc6598ff345034636ac629905477a4297d39a..d7b39d5de29021b78f8a6c3cc62daaa9e0f83fe7 100644 (file)
@@ -98,7 +98,7 @@ DCPSubtitleDecoder::text_subtitles_during (ContentTimePeriod p, bool starting) c
 
        for (list<dcp::SubtitleString>::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
                ContentTimePeriod period = content_time_period (*i);
-               if ((starting && p.contains (period.from)) || (!starting && p.overlaps (period))) {
+               if ((starting && p.contains(period.from)) || (!starting && p.overlap(period))) {
                        d.push_back (period);
                }
        }
index e6ca493b2bb921e58fbe0fb5c7e7f3c23c9aaf4c..a94b605d2b94f61d7a1eebb68d0835d7856174ac 100644 (file)
@@ -28,6 +28,7 @@
 #include "frame_rate_change.h"
 #include "dcpomatic_assert.h"
 #include <locked_sstream.h>
+#include <boost/optional.hpp>
 #include <stdint.h>
 #include <cmath>
 #include <ostream>
@@ -261,8 +262,15 @@ public:
                return TimePeriod<T> (from + o, to + o);
        }
 
-       bool overlaps (TimePeriod<T> const & other) const {
-               return (from < other.to && to > other.from);
+       boost::optional<TimePeriod<T> > overlap (TimePeriod<T> const & other) {
+               T const max_from = std::max (from, other.from);
+               T const min_to = std::min (to, other.to);
+
+               if (max_from >= min_to) {
+                       return boost::optional<TimePeriod<T> > ();
+               }
+
+               return TimePeriod<T> (max_from, min_to);
        }
 
        bool contains (T const & other) const {
index d1992f138530d4de25feedde8539af71c1d3d093..eb870c24164e3bb362b8385d2ff8f6162b8c23d5 100644 (file)
@@ -157,7 +157,7 @@ FFmpegSubtitleStream::subtitles_during (ContentTimePeriod period, bool starting,
 
        /* XXX: inefficient */
        for (map<string, ContentTimePeriod>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
-               if ((starting && period.contains (i->second.from)) || (!starting && period.overlaps (i->second))) {
+               if ((starting && period.contains(i->second.from)) || (!starting && period.overlap(i->second))) {
                        d.push_back (i->second);
                }
        }
index 0138211cf0c49d823f3357fbdca62108a68dbe2c..5e204e126e8d9008fdbc602df42f1336afe06e77 100644 (file)
@@ -1447,3 +1447,9 @@ Film::reels () const
 
        return p;
 }
+
+string
+Film::content_summary (DCPTimePeriod period) const
+{
+       return _playlist->content_summary (period);
+}
index 3063fc761b9d5f8db5b7397b0e11b3b68797196d..d63065f8df3fe6facba393a151e1792388f49e42 100644 (file)
@@ -157,9 +157,13 @@ public:
        }
 
        std::list<DCPTimePeriod> reels () const;
-
        std::list<int> mapped_audio_channels () const;
 
+       /** @param A period within the DCP
+        *  @return Name of the content which most contributes to the given period.
+        */
+       std::string content_summary (DCPTimePeriod period) const;
+
        /** Identifiers for the parts of our state;
            used for signalling changes.
        */
index 9e1b2751763484adc3c3e32b866251b6c75d681e..ccef4cef8ba0cd074b152dd371e7ee40d5ea4466 100644 (file)
@@ -31,7 +31,7 @@ ContentList overlaps (ContentList cl, function<shared_ptr<ContentPart> (shared_p
        ContentList overlaps;
        DCPTimePeriod period (from, to);
        BOOST_FOREACH (shared_ptr<Content> i, cl) {
-               if (part(i) && DCPTimePeriod(i->position(), i->end()).overlap(period)) {
+               if (part(i) && DCPTimePeriod(i->position(), i->end()).overlap(period)) {
                        overlaps.push_back (i);
                }
        }
index 739c5a20b3d0317f506786ce8dbc4fd544a91bab..a8b5a26ebbc0c4e91a9385d2aa67b5a724cf9bb2 100644 (file)
@@ -511,3 +511,28 @@ Playlist::required_disk_space (int j2k_bandwidth, int audio_channels, int audio_
        /* Add on 64k for bits and pieces (metadata, subs etc) */
        return video + audio + 65536;
 }
+
+string
+Playlist::content_summary (DCPTimePeriod period) const
+{
+       string best_summary;
+       int best_score = -1;
+       BOOST_FOREACH (shared_ptr<Content> i, _content) {
+               int score = 0;
+               optional<DCPTimePeriod> const o = DCPTimePeriod(i->position(), i->end()).overlap (period);
+               if (o) {
+                       score += 100 * o.get().duration().get() / period.duration().get();
+               }
+
+               if (i->video) {
+                       score += 100;
+               }
+
+               if (score > best_score) {
+                       best_summary = i->path(0).leaf().string();
+                       best_score = score;
+               }
+       }
+
+       return best_summary;
+}
index 50ae1401a948cf6851e586bf55dc95a6652badf3..e84b51a73e84c91372fb132ca05e59de258ddba4 100644 (file)
@@ -66,6 +66,7 @@ public:
        DCPTime video_end () const;
        DCPTime subtitle_end () const;
        FrameRateChange active_frame_rate_change (DCPTime, int dcp_frame_rate) const;
+       std::string content_summary (DCPTimePeriod period) const;
 
        void set_sequence (bool);
        void maybe_sequence ();
index b712ac3c5a791a1d071af090be051840ff012787..176c54c8f277910c1c63625034e5669e576d191d 100644 (file)
@@ -60,7 +60,9 @@ using dcp::Data;
 
 int const ReelWriter::_info_size = 48;
 
-ReelWriter::ReelWriter (shared_ptr<const Film> film, DCPTimePeriod period, shared_ptr<Job> job, int reel_index, int reel_count)
+ReelWriter::ReelWriter (
+       shared_ptr<const Film> film, DCPTimePeriod period, shared_ptr<Job> job, int reel_index, int reel_count, optional<string> content_summary
+       )
        : _film (film)
        , _period (period)
        , _first_nonexistant_frame (0)
@@ -69,6 +71,7 @@ ReelWriter::ReelWriter (shared_ptr<const Film> film, DCPTimePeriod period, share
        , _total_written_audio_frames (0)
        , _reel_index (reel_index)
        , _reel_count (reel_count)
+       , _content_summary (content_summary)
 {
        /* Create our picture asset in a subdirectory, named according to those
           film's parameters which affect the video output.  We will hard-link
@@ -113,7 +116,7 @@ ReelWriter::ReelWriter (shared_ptr<const Film> film, DCPTimePeriod period, share
                   of the DCP directory until the last minute.
                */
                _sound_asset_writer = _sound_asset->start_write (
-                       _film->directory() / audio_asset_filename (_sound_asset, _reel_index, _reel_count),
+                       _film->directory() / audio_asset_filename (_sound_asset, _reel_index, _reel_count, _content_summary),
                        _film->interop() ? dcp::INTEROP : dcp::SMPTE
                        );
        }
@@ -268,7 +271,7 @@ ReelWriter::finish ()
                boost::filesystem::path video_from = _picture_asset->file ();
                boost::filesystem::path video_to;
                video_to /= _film->dir (_film->dcp_name());
-               video_to /= video_asset_filename (_picture_asset, _reel_index, _reel_count);
+               video_to /= video_asset_filename (_picture_asset, _reel_index, _reel_count, _content_summary);
 
                boost::system::error_code ec;
                boost::filesystem::create_hard_link (video_from, video_to, ec);
@@ -288,7 +291,7 @@ ReelWriter::finish ()
        if (_sound_asset) {
                boost::filesystem::path audio_to;
                audio_to /= _film->dir (_film->dcp_name ());
-               string const aaf = audio_asset_filename (_sound_asset, _reel_index, _reel_count);
+               string const aaf = audio_asset_filename (_sound_asset, _reel_index, _reel_count, _content_summary);
                audio_to /= aaf;
 
                boost::system::error_code ec;
index a80f51dc255c814311ca9d4356e15cba24b5bc95..274f62b9fa0d8cf7af1440a18304e63f118ba857 100644 (file)
@@ -47,7 +47,14 @@ namespace dcp {
 class ReelWriter
 {
 public:
-       ReelWriter (boost::shared_ptr<const Film> film, DCPTimePeriod period, boost::shared_ptr<Job> job, int reel_index, int reel_count);
+       ReelWriter (
+               boost::shared_ptr<const Film> film,
+               DCPTimePeriod period,
+               boost::shared_ptr<Job> job,
+               int reel_index,
+               int reel_count,
+               boost::optional<std::string> content_summary
+               );
 
        void write (boost::optional<dcp::Data> encoded, Frame frame, Eyes eyes);
        void fake_write (Frame frame, Eyes eyes, int size);
@@ -106,6 +113,7 @@ private:
        int _reel_index;
        /** number of reels in the DCP */
        int _reel_count;
+       boost::optional<std::string> _content_summary;
 
        boost::shared_ptr<dcp::PictureAsset> _picture_asset;
        boost::shared_ptr<dcp::PictureAssetWriter> _picture_asset_writer;
index b31b4873ff1a5ef756d26edb0121f38db03d9b09..5ae1a703eb22e40a28b55dd0aff655bec4782755 100644 (file)
@@ -93,7 +93,7 @@ SubtitleDecoder::get (list<T> const & subs, list<ContentTimePeriod> const & sp,
 
        list<T> out;
        for (typename list<T>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
-               if ((starting && period.contains (i->period().from)) || (!starting && period.overlaps (i->period ()))) {
+               if ((starting && period.contains(i->period().from)) || (!starting && period.overlap(i->period()))) {
                        out.push_back (*i);
                }
        }
index ad9c00b63da4962a0d3a3447c567f363721fc862..bec2ab9b7b32029802e2d8dbdb8a5efb62a378a7 100644 (file)
@@ -87,7 +87,7 @@ TextSubtitleDecoder::text_subtitles_during (ContentTimePeriod p, bool starting)
 
        for (vector<sub::Subtitle>::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
                ContentTimePeriod t = content_time_period (*i);
-               if ((starting && p.contains (t.from)) || (!starting && p.overlaps (t))) {
+               if ((starting && p.contains (t.from)) || (!starting && p.overlap (t))) {
                        d.push_back (t);
                }
        }
index c8d0561bec1e19600b4b2535db98d8e27010edd2..a0d6453ff76af2f5d2a44e3a924ebf5092cabfad 100644 (file)
@@ -617,24 +617,30 @@ split_get_request (string url)
 }
 
 string
-video_asset_filename (shared_ptr<dcp::PictureAsset> asset, int reel_index, int reel_count)
+video_asset_filename (shared_ptr<dcp::PictureAsset> asset, int reel_index, int reel_count, optional<string> summary)
 {
        dcp::NameFormat::Map values;
        values['t'] = "j2c";
        values['i'] = asset->id();
        values['r'] = raw_convert<string> (reel_index + 1);
        values['n'] = raw_convert<string> (reel_count);
+       if (summary) {
+               values['c'] = summary.get();
+       }
        return Config::instance()->dcp_filename_format().get(values) + ".mxf";
 }
 
 string
-audio_asset_filename (shared_ptr<dcp::SoundAsset> asset, int reel_index, int reel_count)
+audio_asset_filename (shared_ptr<dcp::SoundAsset> asset, int reel_index, int reel_count, optional<string> summary)
 {
        dcp::NameFormat::Map values;
        values['t'] = "pcm";
        values['i'] = asset->id();
        values['r'] = raw_convert<string> (reel_index + 1);
        values['n'] = raw_convert<string> (reel_count);
+       if (summary) {
+               values['c'] = summary.get();
+       }
        return Config::instance()->dcp_filename_format().get(values) + ".mxf";
 }
 
index b3621bb1304edf52ae7e8fafab1d8367152d3b22..21c0b66614e3a3b9d88f476571443b27818ac68c 100644 (file)
@@ -74,8 +74,8 @@ extern int stride_round_up (int, int const *, int);
 extern void* wrapped_av_malloc (size_t);
 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);
-extern std::string audio_asset_filename (boost::shared_ptr<dcp::SoundAsset> asset, int reel_index, int reel_count);
+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 float relaxed_string_to_float (std::string);
 extern bool string_not_empty (std::string);
 
index 9aee7d92f3099fcd4727d8d1245f55d4173c5b10..a5085abae001c2a4e97077a5f59fb5b608e672f6 100644 (file)
@@ -87,7 +87,7 @@ Writer::Writer (shared_ptr<const Film> film, weak_ptr<Job> j)
        int reel_index = 0;
        list<DCPTimePeriod> const reels = _film->reels ();
        BOOST_FOREACH (DCPTimePeriod p, reels) {
-               _reels.push_back (ReelWriter (film, p, job, reel_index++, reels.size()));
+               _reels.push_back (ReelWriter (film, p, job, reel_index++, reels.size(), _film->content_summary(p)));
        }
 
        /* We can keep track of the current audio and subtitle reels easily because audio
index 6bb216df825bdd840fd021e4ddd1ee20cd6e8451..30c2dbddcf6c46cb9818e9ad977cc88351af493a 100644 (file)
@@ -262,7 +262,7 @@ Timeline::assign_tracks ()
                                shared_ptr<Content> test_content = test->content();
 
                                if (test && test->track() && test->track().get() == t) {
-                                       if (content_period.overlaps (DCPTimePeriod(test_content->position(), test_content->end()))) {
+                                       if (content_period.overlap (DCPTimePeriod(test_content->position(), test_content->end()))) {
                                                /* we have an overlap on track `t' */
                                                ++t;
                                                break;
index 716ec98d756e6547fd0d1c0e7f9d4542e0416182..6dd4094dafbfb85510bea1a7ce719e194741d35a 100644 (file)
@@ -55,18 +55,20 @@ BOOST_AUTO_TEST_CASE (dcpomatic_time_period_overlaps_test)
 
        TimePeriod<DCPTime> a (DCPTime (0), DCPTime (4));
        TimePeriod<DCPTime> b (DCPTime (4), DCPTime (8));
-       BOOST_CHECK (!a.overlaps (b));
+       BOOST_CHECK (!a.overlap (b));
 
        /* Some more obvious non-overlaps */
        a = TimePeriod<DCPTime> (DCPTime (0), DCPTime (4));
        b = TimePeriod<DCPTime> (DCPTime (5), DCPTime (8));
-       BOOST_CHECK (!a.overlaps (b));
+       BOOST_CHECK (!a.overlap (b));
 
        /* Some overlaps */
        a = TimePeriod<DCPTime> (DCPTime (0), DCPTime (4));
        b = TimePeriod<DCPTime> (DCPTime (3), DCPTime (8));
-       BOOST_CHECK (a.overlaps (b));
+       BOOST_CHECK (a.overlap (b));
+       BOOST_CHECK_EQUAL (a.overlap(b).get(), DCPTimePeriod(DCPTime(3), DCPTime(4)));
        a = TimePeriod<DCPTime> (DCPTime (1), DCPTime (9));
        b = TimePeriod<DCPTime> (DCPTime (0), DCPTime (10));
-       BOOST_CHECK (a.overlaps (b));
+       BOOST_CHECK (a.overlap (b));
+       BOOST_CHECK_EQUAL (a.overlap(b).get(), DCPTimePeriod(DCPTime(1), DCPTime(9)));
 }
diff --git a/test/file_naming_test.cc b/test/file_naming_test.cc
new file mode 100644 (file)
index 0000000..4c2b0af
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+    Copyright (C) 2016 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 "test.h"
+#include "lib/config.h"
+#include "lib/film.h"
+#include "lib/ffmpeg_content.h"
+#include "lib/dcp_content_type.h"
+#include <boost/test/unit_test.hpp>
+
+using boost::shared_ptr;
+
+BOOST_AUTO_TEST_CASE (file_naming_test)
+{
+       dcp::FilenameFormat nf ("%c");
+       Config::instance()->set_dcp_filename_format (dcp::FilenameFormat ("%c"));
+       shared_ptr<Film> film = new_test_film ("file_naming_test");
+       film->set_name ("file_naming_test");
+       film->set_dcp_content_type (DCPContentType::from_isdcf_name ("FTR"));
+       shared_ptr<FFmpegContent> r (new FFmpegContent (film, "test/data/flat_red.png"));
+       film->examine_and_add_content (r);
+       shared_ptr<FFmpegContent> g (new FFmpegContent (film, "test/data/flat_green.png"));
+       film->examine_and_add_content (g);
+       shared_ptr<FFmpegContent> b (new FFmpegContent (film, "test/data/flat_blue.png"));
+       film->examine_and_add_content (b);
+       wait_for_jobs ();
+
+       film->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
+       film->make_dcp ();
+       wait_for_jobs ();
+
+       BOOST_CHECK (boost::filesystem::exists (film->file (film->dcp_name() + "/flat_red.png.mxf")));
+       BOOST_CHECK (boost::filesystem::exists (film->file (film->dcp_name() + "/flat_green.png.mxf")));
+       BOOST_CHECK (boost::filesystem::exists (film->file (film->dcp_name() + "/flat_blue.png.mxf")));
+}
index 49e874ea9dfee4fcdf4ab4ad4958455645abe337..a7a406856c80b30b6b86a3c15b3c4109f08eb970 100644 (file)
@@ -63,6 +63,7 @@ def build(bld):
                  ffmpeg_pts_offset_test.cc
                  file_group_test.cc
                  file_log_test.cc
+                 file_naming_test.cc
                  film_metadata_test.cc
                  frame_rate_test.cc
                  image_filename_sorter_test.cc