Tidy up careful_string_filter and add some extra transliterations.
[dcpomatic.git] / src / lib / reel_writer.cc
index 0b367ae38581a15649b9e3eef7de8c545561c1cf..94920b95bce0cd82232e94c9aca0e9275ccf70c1 100644 (file)
 
 */
 
-#include "reel_writer.h"
-#include "film.h"
+
+#include "audio_buffers.h"
+#include "compose.hpp"
+#include "config.h"
 #include "cross.h"
-#include "job.h"
-#include "log.h"
 #include "dcpomatic_log.h"
 #include "digester.h"
+#include "film.h"
 #include "font_data.h"
-#include "compose.hpp"
-#include "config.h"
-#include "audio_buffers.h"
 #include "image.h"
+#include "image_png.h"
+#include "job.h"
+#include "log.h"
+#include "reel_writer.h"
 #include <dcp/atmos_asset.h>
 #include <dcp/atmos_asset_writer.h>
 #include <dcp/certificate_chain.h>
 
 #include "i18n.h"
 
-using std::list;
-using std::string;
-using std::cout;
+
+using std::dynamic_pointer_cast;
 using std::exception;
+using std::list;
+using std::make_shared;
 using std::map;
 using std::set;
-using std::vector;
 using std::shared_ptr;
-using std::make_shared;
+using std::string;
+using std::vector;
+using std::weak_ptr;
 using boost::optional;
-using std::dynamic_pointer_cast;
 #if BOOST_VERSION >= 106100
 using namespace boost::placeholders;
 #endif
-using std::weak_ptr;
 using dcp::ArrayData;
 using dcp::Data;
 using dcp::raw_convert;
 using namespace dcpomatic;
 
+
 int const ReelWriter::_info_size = 48;
 
+
 static dcp::MXFMetadata
 mxf_metadata ()
 {
@@ -95,6 +99,7 @@ mxf_metadata ()
        return meta;
 }
 
+
 /** @param job Related job, or 0.
  *  @param text_only true to enable a special mode where the writer will expect only subtitles and closed captions to be written
  *  (no picture nor sound) and not give errors in that case.  This is used by the hints system to check the potential sizes of
@@ -168,12 +173,12 @@ ReelWriter::ReelWriter (
        }
 
        if (film()->audio_channels()) {
-               auto langs = film()->audio_languages();
+               auto lang = film()->audio_language();
                _sound_asset = make_shared<dcp::SoundAsset> (
                        dcp::Fraction(film()->video_frame_rate(), 1),
                        film()->audio_frame_rate(),
                        film()->audio_channels(),
-                       langs.empty() ? dcp::LanguageTag("en-US") : langs.front(),
+                       lang ? *lang : dcp::LanguageTag("en-US"),
                        standard
                        );
 
@@ -197,33 +202,36 @@ ReelWriter::ReelWriter (
        _default_font = dcp::ArrayData(default_font_file());
 }
 
+
 /** @param frame reel-relative frame */
 void
 ReelWriter::write_frame_info (Frame frame, Eyes eyes, dcp::FrameInfo info) const
 {
        auto handle = film()->info_file_handle(_period, false);
-       dcpomatic_fseek (handle->get(), frame_info_position(frame, eyes), SEEK_SET);
-       checked_fwrite (&info.offset, sizeof(info.offset), handle->get(), handle->file());
-       checked_fwrite (&info.size, sizeof (info.size), handle->get(), handle->file());
-       checked_fwrite (info.hash.c_str(), info.hash.size(), handle->get(), handle->file());
+       handle->get().seek(frame_info_position(frame, eyes), SEEK_SET);
+       handle->get().checked_write(&info.offset, sizeof(info.offset));
+       handle->get().checked_write(&info.size, sizeof(info.size));
+       handle->get().checked_write(info.hash.c_str(), info.hash.size());
 }
 
+
 dcp::FrameInfo
 ReelWriter::read_frame_info (shared_ptr<InfoFileHandle> info, Frame frame, Eyes eyes) const
 {
        dcp::FrameInfo frame_info;
-       dcpomatic_fseek (info->get(), frame_info_position(frame, eyes), SEEK_SET);
-       checked_fread (&frame_info.offset, sizeof(frame_info.offset), info->get(), info->file());
-       checked_fread (&frame_info.size, sizeof(frame_info.size), info->get(), info->file());
+       info->get().seek(frame_info_position(frame, eyes), SEEK_SET);
+       info->get().checked_read(&frame_info.offset, sizeof(frame_info.offset));
+       info->get().checked_read(&frame_info.size, sizeof(frame_info.size));
 
        char hash_buffer[33];
-       checked_fread (hash_buffer, 32, info->get(), info->file());
+       info->get().checked_read(hash_buffer, 32);
        hash_buffer[32] = '\0';
        frame_info.hash = hash_buffer;
 
        return frame_info;
 }
 
+
 long
 ReelWriter::frame_info_position (Frame frame, Eyes eyes) const
 {
@@ -241,6 +249,7 @@ ReelWriter::frame_info_position (Frame frame, Eyes eyes) const
        DCPOMATIC_ASSERT (false);
 }
 
+
 Frame
 ReelWriter::check_existing_picture_asset (boost::filesystem::path asset)
 {
@@ -251,7 +260,7 @@ ReelWriter::check_existing_picture_asset (boost::filesystem::path asset)
        }
 
        /* Try to open the existing asset */
-       auto asset_file = fopen_boost (asset, "rb");
+       dcp::File asset_file(asset, "rb");
        if (!asset_file) {
                LOG_GENERAL ("Could not open existing asset at %1 (errno=%2)", asset.string(), errno);
                return 0;
@@ -265,13 +274,12 @@ ReelWriter::check_existing_picture_asset (boost::filesystem::path asset)
                info_file = film()->info_file_handle (_period, true);
        } catch (OpenFileError &) {
                LOG_GENERAL_NC ("Could not open film info file");
-               fclose (asset_file);
                return 0;
        }
 
        /* Offset of the last dcp::FrameInfo in the info file */
-       int const n = (boost::filesystem::file_size(info_file->file()) / _info_size) - 1;
-       LOG_GENERAL ("The last FI is %1; info file is %2, info size %3", n, boost::filesystem::file_size(info_file->file()), _info_size);
+       int const n = (boost::filesystem::file_size(info_file->get().path()) / _info_size) - 1;
+       LOG_GENERAL ("The last FI is %1; info file is %2, info size %3", n, boost::filesystem::file_size(info_file->get().path()), _info_size);
 
        Frame first_nonexistant_frame;
        if (film()->three_d()) {
@@ -294,11 +302,10 @@ ReelWriter::check_existing_picture_asset (boost::filesystem::path asset)
 
        LOG_GENERAL ("Proceeding with first nonexistant frame %1", first_nonexistant_frame);
 
-       fclose (asset_file);
-
        return first_nonexistant_frame;
 }
 
+
 void
 ReelWriter::write (shared_ptr<const Data> encoded, Frame frame, Eyes eyes)
 {
@@ -340,6 +347,7 @@ ReelWriter::fake_write (int size)
        _picture_asset_writer->fake_write (size);
 }
 
+
 void
 ReelWriter::repeat_write (Frame frame, Eyes eyes)
 {
@@ -355,6 +363,7 @@ ReelWriter::repeat_write (Frame frame, Eyes eyes)
        write_frame_info (frame, eyes, fin);
 }
 
+
 void
 ReelWriter::finish (boost::filesystem::path output_dcp)
 {
@@ -440,6 +449,10 @@ ReelWriter::finish (boost::filesystem::path output_dcp)
        }
 }
 
+
+/** Try to make a ReelAsset for a subtitles or closed captions in a given period in the DCP.
+ *  A SubtitleAsset can be provided, or we will use one from @ref refs if not.
+ */
 template <class Interop, class SMPTE, class Result>
 shared_ptr<Result>
 maybe_add_text (
@@ -641,7 +654,7 @@ ReelWriter::create_reel_text (
        } else if (ensure_subtitles) {
                /* We had no subtitle asset, but we've been asked to make sure there is one */
                subtitle = maybe_add_text<dcp::ReelInteropSubtitleAsset, dcp::ReelSMPTESubtitleAsset, dcp::ReelSubtitleAsset> (
-                       empty_text_asset(TextType::OPEN_SUBTITLE, optional<DCPTextTrack>()),
+                       empty_text_asset(TextType::OPEN_SUBTITLE, optional<DCPTextTrack>(), true),
                        duration,
                        reel,
                        refs,
@@ -670,7 +683,7 @@ ReelWriter::create_reel_text (
        /* Make empty tracks for anything we've been asked to ensure but that we haven't added */
        for (auto i: ensure_closed_captions) {
                auto a = maybe_add_text<dcp::ReelInteropClosedCaptionAsset, dcp::ReelSMPTEClosedCaptionAsset, dcp::ReelClosedCaptionAsset> (
-                       empty_text_asset(TextType::CLOSED_CAPTION, i), duration, reel, refs, fonts, _default_font, film(), _period, output_dcp, _text_only
+                       empty_text_asset(TextType::CLOSED_CAPTION, i, true), duration, reel, refs, fonts, _default_font, film(), _period, output_dcp, _text_only
                        );
                DCPOMATIC_ASSERT (a);
                a->set_annotation_text (i.name);
@@ -681,7 +694,6 @@ ReelWriter::create_reel_text (
 }
 
 
-
 void
 ReelWriter::create_reel_markers (shared_ptr<dcp::Reel> reel) const
 {
@@ -695,7 +707,7 @@ ReelWriter::create_reel_markers (shared_ptr<dcp::Reel> reel) const
        }
 
        if (!reel_markers.empty ()) {
-               auto ma = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(film()->video_frame_rate(), 1), reel->duration(), 0);
+               auto ma = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(film()->video_frame_rate(), 1), reel->duration());
                for (auto const& i: reel_markers) {
                        DCPTime relative = i.second - _period.from;
                        auto hmsf = relative.split (film()->video_frame_rate());
@@ -764,6 +776,7 @@ try
         */
 }
 
+
 Frame
 ReelWriter::start () const
 {
@@ -784,7 +797,7 @@ ReelWriter::write (shared_ptr<const AudioBuffers> audio)
 
 
 shared_ptr<dcp::SubtitleAsset>
-ReelWriter::empty_text_asset (TextType type, optional<DCPTextTrack> track) const
+ReelWriter::empty_text_asset (TextType type, optional<DCPTextTrack> track, bool with_dummy) const
 {
        shared_ptr<dcp::SubtitleAsset> asset;
 
@@ -815,29 +828,32 @@ ReelWriter::empty_text_asset (TextType type, optional<DCPTextTrack> track) const
                if (film()->encrypted()) {
                        s->set_key (film()->key());
                }
-               s->add (
-                       std::make_shared<dcp::SubtitleString>(
-                               optional<std::string>(),
-                               false,
-                               false,
-                               false,
-                               dcp::Colour(),
-                               42,
-                               1.0,
-                               dcp::Time(0, 0, 0, 0, 24),
-                               dcp::Time(0, 0, 1, 0, 24),
-                               0.5,
-                               dcp::HAlign::CENTER,
-                               0.5,
-                               dcp::VAlign::CENTER,
-                               dcp::Direction::LTR,
-                               "",
-                               dcp::Effect::NONE,
-                               dcp::Colour(),
-                               dcp::Time(),
-                               dcp::Time()
-                               )
-                      );
+               if (with_dummy) {
+                       s->add (
+                               std::make_shared<dcp::SubtitleString>(
+                                       optional<std::string>(),
+                                       false,
+                                       false,
+                                       false,
+                                       dcp::Colour(),
+                                       42,
+                                       1.0,
+                                       dcp::Time(0, 0, 0, 0, 24),
+                                       dcp::Time(0, 0, 1, 0, 24),
+                                       0.5,
+                                       dcp::HAlign::CENTER,
+                                       0.5,
+                                       dcp::VAlign::CENTER,
+                                       dcp::Direction::LTR,
+                                       " ",
+                                       dcp::Effect::NONE,
+                                       dcp::Colour(),
+                                       dcp::Time(),
+                                       dcp::Time(),
+                                       0
+                                       )
+                              );
+               }
                asset = s;
        }
 
@@ -863,7 +879,7 @@ ReelWriter::write (PlayerText subs, TextType type, optional<DCPTextTrack> track,
        }
 
        if (!asset) {
-               asset = empty_text_asset (type, track);
+               asset = empty_text_asset (type, track, false);
        }
 
        switch (type) {
@@ -890,7 +906,7 @@ ReelWriter::write (PlayerText subs, TextType type, optional<DCPTextTrack> track,
        for (auto i: subs.bitmap) {
                asset->add (
                        make_shared<dcp::SubtitleImage>(
-                               i.image->as_png(),
+                               image_as_png(i.image),
                                dcp::Time(period.from.seconds() - _period.from.seconds(), tcr),
                                dcp::Time(period.to.seconds() - _period.from.seconds(), tcr),
                                i.rectangle.x, dcp::HAlign::LEFT, i.rectangle.y, dcp::VAlign::TOP,
@@ -900,8 +916,9 @@ ReelWriter::write (PlayerText subs, TextType type, optional<DCPTextTrack> track,
        }
 }
 
+
 bool
-ReelWriter::existing_picture_frame_ok (FILE* asset_file, shared_ptr<InfoFileHandle> info_file, Frame frame) const
+ReelWriter::existing_picture_frame_ok (dcp::File& asset_file, shared_ptr<InfoFileHandle> info_file, Frame frame) const
 {
        LOG_GENERAL ("Checking existing picture frame %1", frame);
 
@@ -913,9 +930,9 @@ ReelWriter::existing_picture_frame_ok (FILE* asset_file, shared_ptr<InfoFileHand
        bool ok = true;
 
        /* Read the data from the asset and hash it */
-       dcpomatic_fseek (asset_file, info.offset, SEEK_SET);
+       asset_file.seek(info.offset, SEEK_SET);
        ArrayData data (info.size);
-       size_t const read = fread (data.data(), 1, data.size(), asset_file);
+       size_t const read = asset_file.read(data.data(), 1, data.size());
        LOG_GENERAL ("Read %1 bytes of asset data; wanted %2", read, info.size);
        if (read != static_cast<size_t> (data.size ())) {
                LOG_GENERAL ("Existing frame %1 is incomplete", frame);