Move locale_convert into libdcp.
[dcpomatic.git] / src / lib / film.cc
index db8da56b49e3ec748f54caf8ee49dd8ebc5269b3..a41ab3c95eb2c7722418ec814185a88c06e59701 100644 (file)
@@ -38,9 +38,7 @@
 #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 "digester.h"
 #include "compose.hpp"
 #include <dcp/util.h>
 #include <dcp/local_time.h>
 #include <dcp/decrypted_kdm.h>
+#include <dcp/raw_convert.h>
 #include <libxml++/libxml++.h>
 #include <boost/filesystem.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/foreach.hpp>
+#include <boost/regex.hpp>
 #include <unistd.h>
 #include <stdexcept>
 #include <iostream>
@@ -88,6 +88,7 @@ using boost::weak_ptr;
 using boost::dynamic_pointer_cast;
 using boost::optional;
 using boost::is_any_of;
+using dcp::raw_convert;
 
 #define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
 #define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, LogEntry::TYPE_GENERAL);
@@ -111,8 +112,12 @@ using boost::is_any_of;
  * 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 = 34;
+int const Film::current_state_version = 36;
 
 /** Construct a Film object in a given directory.
  *
@@ -130,7 +135,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 ())
@@ -192,32 +197,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 */
@@ -359,7 +361,7 @@ Film::metadata () const
        if (_audio_processor) {
                root->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
        }
-       root->add_child("ReelType")->add_child_text (raw_convert<string> (_reel_type));
+       root->add_child("ReelType")->add_child_text (raw_convert<string> (static_cast<int> (_reel_type)));
        root->add_child("ReelLength")->add_child_text (raw_convert<string> (_reel_length));
        root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
        _playlist->as_xml (root->add_child ("Playlist"));
@@ -525,7 +527,7 @@ Film::mapped_audio_channels () const
 string
 Film::isdcf_name (bool if_created_now) const
 {
-       SafeStringStream d;
+       string d;
 
        string raw_name = name ();
 
@@ -568,49 +570,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 */
@@ -631,12 +633,12 @@ Film::isdcf_name (bool if_created_now) const
                }
 
                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;
@@ -657,18 +659,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;
                }
        }
 
@@ -691,35 +693,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;
@@ -731,12 +733,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 */
@@ -917,23 +919,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);
 }
 
@@ -1290,18 +1292,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) {
@@ -1376,7 +1404,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()) {
@@ -1417,3 +1444,9 @@ Film::reels () const
 
        return p;
 }
+
+string
+Film::content_summary (DCPTimePeriod period) const
+{
+       return _playlist->content_summary (period);
+}