Remove all use of stringstream in an attempt to fix
[dcpomatic.git] / src / lib / film.cc
index 7d319fc1e02e6882e8638a3c3f51cb1bc87f97a5..28017669bd035afa2e7cb00327c23c27888e62da 100644 (file)
@@ -1,19 +1,20 @@
 /*
     Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
 
-    This program is free software; you can redistribute it and/or modify
+    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.
 
-    This program is distributed in the hope that it will be useful,
+    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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 
 #include "dcp_content_type.h"
 #include "ratio.h"
 #include "cross.h"
-#include "safe_stringstream.h"
 #include "environment_info.h"
 #include "raw_convert.h"
 #include "audio_processor.h"
-#include "md5_digester.h"
+#include "digester.h"
 #include "compose.hpp"
 #include "screen.h"
 #include "audio_content.h"
@@ -61,6 +61,7 @@
 #include <boost/filesystem.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/foreach.hpp>
+#include <boost/regex.hpp>
 #include <unistd.h>
 #include <stdexcept>
 #include <iostream>
@@ -107,8 +108,15 @@ using boost::is_any_of;
  *
  * 32 -> 33
  * Changed <Period> to <Subtitle> in FFmpegSubtitleStream
+ * 33 -> 34
+ * Content only contains audio/subtitle-related tags if those things
+ * are present.
+ * 34 -> 35
+ * VideoFrameType in VideoContent is a string rather than an integer.
+ * 35 -> 36
+ * EffectColour rather than OutlineColour in Subtitle.
  */
-int const Film::current_state_version = 33;
+int const Film::current_state_version = 36;
 
 /** Construct a Film object in a given directory.
  *
@@ -126,7 +134,7 @@ Film::Film (boost::filesystem::path dir, bool log)
        , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
        , _isdcf_metadata (Config::instance()->default_isdcf_metadata ())
        , _video_frame_rate (24)
-       , _audio_channels (6)
+       , _audio_channels (Config::instance()->default_dcp_audio_channels ())
        , _three_d (false)
        , _sequence (true)
        , _interop (Config::instance()->default_interop ())
@@ -188,32 +196,29 @@ Film::video_identifier () const
 {
        DCPOMATIC_ASSERT (container ());
 
-       SafeStringStream s;
-       s.imbue (std::locale::classic ());
-
-       s << container()->id()
-         << "_" << resolution_to_string (_resolution)
-         << "_" << _playlist->video_identifier()
-         << "_" << _video_frame_rate
-         << "_" << j2k_bandwidth();
+       string s = container()->id()
+               + "_" + resolution_to_string (_resolution)
+               + "_" + _playlist->video_identifier()
+               + "_" + raw_convert<string>(_video_frame_rate)
+               + "_" + raw_convert<string>(j2k_bandwidth());
 
        if (encrypted ()) {
-               s << "_E";
+               s += "_E";
        } else {
-               s << "_P";
+               s += "_P";
        }
 
        if (_interop) {
-               s << "_I";
+               s += "_I";
        } else {
-               s << "_S";
+               s += "_S";
        }
 
        if (_three_d) {
-               s << "_3D";
+               s += "_3D";
        }
 
-       return s.str ();
+       return s;
 }
 
 /** @return The file to write video frame info to */
@@ -243,15 +248,14 @@ Film::audio_analysis_path (shared_ptr<const Playlist> playlist) const
 {
        boost::filesystem::path p = dir ("analysis");
 
-       MD5Digester digester;
+       Digester digester;
        BOOST_FOREACH (shared_ptr<Content> i, playlist->content ()) {
-               shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (i);
-               if (!ac) {
+               if (!i->audio) {
                        continue;
                }
 
-               digester.add (ac->digest ());
-               digester.add (ac->audio_mapping().digest ());
+               digester.add (i->digest ());
+               digester.add (i->audio->mapping().digest ());
                if (playlist->content().size() != 1) {
                        /* Analyses should be considered equal regardless of gain
                           if they were made from just one piece of content.  This
@@ -259,7 +263,7 @@ Film::audio_analysis_path (shared_ptr<const Playlist> playlist) const
                           analysis at the plotting stage rather than having to
                           recompute it.
                        */
-                       digester.add (ac->audio_gain ());
+                       digester.add (i->audio->gain ());
                }
        }
 
@@ -505,9 +509,8 @@ Film::mapped_audio_channels () const
                }
        } else {
                BOOST_FOREACH (shared_ptr<Content> i, content ()) {
-                       shared_ptr<const AudioContent> ac = dynamic_pointer_cast<const AudioContent> (i);
-                       if (ac) {
-                               list<int> c = ac->audio_mapping().mapped_output_channels ();
+                       if (i->audio) {
+                               list<int> c = i->audio->mapping().mapped_output_channels ();
                                copy (c.begin(), c.end(), back_inserter (mapped));
                        }
                }
@@ -523,7 +526,7 @@ Film::mapped_audio_channels () const
 string
 Film::isdcf_name (bool if_created_now) const
 {
-       SafeStringStream d;
+       string d;
 
        string raw_name = name ();
 
@@ -566,49 +569,49 @@ Film::isdcf_name (bool if_created_now) const
                fixed_name = fixed_name.substr (0, 14);
        }
 
-       d << fixed_name;
+       d += fixed_name;
 
        if (dcp_content_type()) {
-               d << "_" << dcp_content_type()->isdcf_name();
-               d << "-" << isdcf_metadata().content_version;
+               d += "_" + dcp_content_type()->isdcf_name();
+               d += "-" + raw_convert<string>(isdcf_metadata().content_version);
        }
 
        ISDCFMetadata const dm = isdcf_metadata ();
 
        if (dm.temp_version) {
-               d << "-Temp";
+               d += "-Temp";
        }
 
        if (dm.pre_release) {
-               d << "-Pre";
+               d += "-Pre";
        }
 
        if (dm.red_band) {
-               d << "-RedBand";
+               d += "-RedBand";
        }
 
        if (!dm.chain.empty ()) {
-               d << "-" << dm.chain;
+               d += "-" + dm.chain;
        }
 
        if (three_d ()) {
-               d << "-3D";
+               d += "-3D";
        }
 
        if (dm.two_d_version_of_three_d) {
-               d << "-2D";
+               d += "-2D";
        }
 
        if (!dm.mastered_luminance.empty ()) {
-               d << "-" << dm.mastered_luminance;
+               d += "-" + dm.mastered_luminance;
        }
 
        if (video_frame_rate() != 24) {
-               d << "-" << video_frame_rate();
+               d += "-" + raw_convert<string>(video_frame_rate());
        }
 
        if (container()) {
-               d << "_" << container()->isdcf_name();
+               d += "_" + container()->isdcf_name();
        }
 
        /* XXX: this uses the first bit of content only */
@@ -622,29 +625,28 @@ Film::isdcf_name (bool if_created_now) const
                                if (i->video->scale().ratio ()) {
                                        content_ratio = i->video->scale().ratio ();
                                } else {
-                                       content_ratio = Ratio::from_ratio (i->video->video_size().ratio ());
+                                       content_ratio = Ratio::from_ratio (i->video->size().ratio ());
                                }
                                break;
                        }
                }
 
                if (content_ratio && content_ratio != container()) {
-                       d << "-" << content_ratio->isdcf_name();
+                       d += "-" + content_ratio->isdcf_name();
                }
        }
 
        if (!dm.audio_language.empty ()) {
-               d << "_" << dm.audio_language;
+               d += "_" + dm.audio_language;
                if (!dm.subtitle_language.empty()) {
 
                        bool burnt_in = true;
                        BOOST_FOREACH (shared_ptr<Content> i, content ()) {
-                               shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (i);
-                               if (!sc) {
+                               if (!i->subtitle) {
                                        continue;
                                }
 
-                               if (sc->use_subtitles() && !sc->burn_subtitles()) {
+                               if (i->subtitle->use() && !i->subtitle->burn()) {
                                        burnt_in = false;
                                }
                        }
@@ -656,18 +658,18 @@ Film::isdcf_name (bool if_created_now) const
                                transform (language.begin(), language.end(), language.begin(), ::toupper);
                        }
 
-                       d << "-" << language;
+                       d += "-" + language;
                } else {
-                       d << "-XX";
+                       d += "-XX";
                }
        }
 
        if (!dm.territory.empty ()) {
-               d << "_" << dm.territory;
+               d += "_" + dm.territory;
                if (dm.rating.empty ()) {
-                       d << "-NR";
+                       d += "-NR";
                } else {
-                       d << "-" << dm.rating;
+                       d += "-" + dm.rating;
                }
        }
 
@@ -690,35 +692,35 @@ Film::isdcf_name (bool if_created_now) const
        }
 
        if (non_lfe) {
-               d << "_" << non_lfe << lfe;
+               d += String::compose("_%1%2", non_lfe, lfe);
        }
 
        /* XXX: HI/VI */
 
-       d << "_" << resolution_to_string (_resolution);
+       d += "_" + resolution_to_string (_resolution);
 
        if (!dm.studio.empty ()) {
-               d << "_" << dm.studio;
+               d += "_" + dm.studio;
        }
 
        if (if_created_now) {
-               d << "_" << boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
+               d += "_" + boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
        } else {
-               d << "_" << boost::gregorian::to_iso_string (_isdcf_date);
+               d += "_" + boost::gregorian::to_iso_string (_isdcf_date);
        }
 
        if (!dm.facility.empty ()) {
-               d << "_" << dm.facility;
+               d += "_" + dm.facility;
        }
 
        if (_interop) {
-               d << "_IOP";
+               d += "_IOP";
        } else {
-               d << "_SMPTE";
+               d += "_SMPTE";
        }
 
        if (three_d ()) {
-               d << "-3D";
+               d += "-3D";
        }
 
        bool vf = false;
@@ -730,12 +732,12 @@ Film::isdcf_name (bool if_created_now) const
        }
 
        if (vf) {
-               d << "_VF";
+               d += "_VF";
        } else {
-               d << "_OV";
+               d += "_OV";
        }
 
-       return d.str ();
+       return d;
 }
 
 /** @return name to give the DCP */
@@ -890,7 +892,7 @@ Film::signal_changed (Property p)
 
        switch (p) {
        case Film::CONTENT:
-               set_video_frame_rate (_playlist->best_dcp_frame_rate ());
+               set_video_frame_rate (_playlist->best_video_frame_rate ());
                break;
        case Film::VIDEO_FRAME_RATE:
        case Film::SEQUENCE:
@@ -916,23 +918,23 @@ Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
        p /= "j2c";
        p /= video_identifier ();
 
-       SafeStringStream s;
-       s.width (8);
-       s << setfill('0') << reel << "_" << frame;
+       char buffer[256];
+       snprintf(buffer, sizeof(buffer), "%08d_%08" PRId64, reel, frame);
+       string s (buffer);
 
        if (eyes == EYES_LEFT) {
-               s << ".L";
+               s += ".L";
        } else if (eyes == EYES_RIGHT) {
-               s << ".R";
+               s += ".R";
        }
 
-       s << ".j2c";
+       s += ".j2c";
 
        if (tmp) {
-               s << ".tmp";
+               s += ".tmp";
        }
 
-       p /= s.str();
+       p /= s;
        return file (p);
 }
 
@@ -1033,7 +1035,7 @@ Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
        }
 
        add_content (content);
-       if (Config::instance()->automatic_audio_analysis() && dynamic_pointer_cast<AudioContent> (content)) {
+       if (Config::instance()->automatic_audio_analysis() && content->audio) {
                shared_ptr<Playlist> playlist (new Playlist);
                playlist->add (content);
                boost::signals2::connection c;
@@ -1050,7 +1052,7 @@ Film::add_content (shared_ptr<Content> c)
        /* Add {video,subtitle} content after any existing {video,subtitle} content */
        if (c->video) {
                c->set_position (_playlist->video_end ());
-       } else if (dynamic_pointer_cast<SubtitleContent> (c)) {
+       } else if (c->subtitle) {
                c->set_position (_playlist->subtitle_end ());
        }
 
@@ -1085,7 +1087,7 @@ Film::length () const
 int
 Film::best_video_frame_rate () const
 {
-       return _playlist->best_dcp_frame_rate ();
+       return _playlist->best_video_frame_rate ();
 }
 
 FrameRateChange
@@ -1099,9 +1101,9 @@ Film::playlist_content_changed (weak_ptr<Content> c, int p, bool frequent)
 {
        _dirty = true;
 
-       if (p == VideoContentProperty::VIDEO_FRAME_RATE) {
-               set_video_frame_rate (_playlist->best_dcp_frame_rate ());
-       } else if (p == AudioContentProperty::AUDIO_STREAMS) {
+       if (p == ContentProperty::VIDEO_FRAME_RATE) {
+               set_video_frame_rate (_playlist->best_video_frame_rate ());
+       } else if (p == AudioContentProperty::STREAMS) {
                signal_changed (NAME);
        }
 
@@ -1125,8 +1127,7 @@ int
 Film::audio_frame_rate () const
 {
        BOOST_FOREACH (shared_ptr<Content> i, content ()) {
-               shared_ptr<AudioContent> a = dynamic_pointer_cast<AudioContent> (i);
-               if (a && a->has_rate_above_48k ()) {
+               if (i->audio && i->audio->has_rate_above_48k ()) {
                        return 96000;
                }
        }
@@ -1271,9 +1272,8 @@ Film::subtitle_language () const
 
        ContentList cl = content ();
        BOOST_FOREACH (shared_ptr<Content>& c, cl) {
-               shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (c);
-               if (sc) {
-                       languages.insert (sc->subtitle_language ());
+               if (c->subtitle) {
+                       languages.insert (c->subtitle->language ());
                }
        }
 
@@ -1291,18 +1291,44 @@ Film::subtitle_language () const
 
 /** Change the gains of the supplied AudioMapping to make it a default
  *  for this film.  The defaults are guessed based on what processor (if any)
- *  is in use and the number of input channels.
+ *  is in use, the number of input channels and any filename supplied.
  */
 void
-Film::make_audio_mapping_default (AudioMapping& mapping) const
+Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
 {
+       static string const regex[] = {
+               ".*[\\._-]L[\\._-].*",
+               ".*[\\._-]R[\\._-].*",
+               ".*[\\._-]C[\\._-].*",
+               ".*[\\._-]Lfe[\\._-].*",
+               ".*[\\._-]Ls[\\._-].*",
+               ".*[\\._-]Rs[\\._-].*"
+       };
+
+       static int const regexes = sizeof(regex) / sizeof(*regex);
+
        if (audio_processor ()) {
                audio_processor()->make_audio_mapping_default (mapping);
        } else {
                mapping.make_zero ();
                if (mapping.input_channels() == 1) {
-                       /* Mono -> Centre */
-                       mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
+                       bool guessed = false;
+
+                       /* See if we can guess where this stream should go */
+                       if (filename) {
+                               for (int i = 0; i < regexes; ++i) {
+                                       boost::regex e (regex[i], boost::regex::icase);
+                                       if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
+                                               mapping.set (0, i, 1);
+                                               guessed = true;
+                                       }
+                               }
+                       }
+
+                       if (!guessed) {
+                               /* If we have no idea, just put it on centre */
+                               mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
+                       }
                } else {
                        /* 1:1 mapping */
                        for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
@@ -1377,7 +1403,6 @@ Film::reels () const
        {
                optional<DCPTime> last_split;
                shared_ptr<Content> last_video;
-               ContentList cl = content ();
                BOOST_FOREACH (shared_ptr<Content> c, content ()) {
                        if (c->video) {
                                BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
@@ -1418,3 +1443,9 @@ Film::reels () const
 
        return p;
 }
+
+string
+Film::content_summary (DCPTimePeriod period) const
+{
+       return _playlist->content_summary (period);
+}