X-Git-Url: https://main.carlh.net/gitweb/?p=dcpomatic.git;a=blobdiff_plain;f=src%2Flib%2Fwriter.cc;h=699f220c4df31fdef6dfbae02086515119e60fd6;hp=da1a0768395b1963097cd85886c2b21cd210553f;hb=93f6b7f36dba0eed49936dcd014a9cf5941a66ac;hpb=c78d83c56bdec4935efd7a7d2f758165873d1137 diff --git a/src/lib/writer.cc b/src/lib/writer.cc index da1a07683..699f220c4 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2018 Carl Hetherington + Copyright (C) 2012-2019 Carl Hetherington This file is part of DCP-o-matic. @@ -23,6 +23,7 @@ #include "film.h" #include "ratio.h" #include "log.h" +#include "dcpomatic_log.h" #include "dcp_video.h" #include "dcp_content_type.h" #include "audio_mapping.h" @@ -34,6 +35,7 @@ #include "font.h" #include "util.h" #include "reel_writer.h" +#include "text_content.h" #include #include #include @@ -44,14 +46,6 @@ #include "i18n.h" -#define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL); -#define LOG_GENERAL_NC(...) _film->log()->log (__VA_ARGS__, LogEntry::TYPE_GENERAL); -#define LOG_DEBUG_ENCODE(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_DEBUG_ENCODE); -#define LOG_TIMING(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_TIMING); -#define LOG_WARNING_NC(...) _film->log()->log (__VA_ARGS__, LogEntry::TYPE_WARNING); -#define LOG_WARNING(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_WARNING); -#define LOG_ERROR(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_ERROR); - /* OS X strikes again */ #undef set_key @@ -69,11 +63,11 @@ using boost::weak_ptr; using boost::dynamic_pointer_cast; using boost::optional; using dcp::Data; +using namespace dcpomatic; Writer::Writer (shared_ptr film, weak_ptr j) : _film (film) , _job (j) - , _thread (0) , _finish (false) , _queued_full_in_memory (0) /* These will be reset to sensible values when J2KEncoder is created */ @@ -93,6 +87,8 @@ Writer::Writer (shared_ptr film, weak_ptr j) _reels.push_back (ReelWriter (film, p, job, reel_index++, reels.size(), _film->content_summary(p))); } + _last_written.resize (reels.size()); + /* We can keep track of the current audio, subtitle and closed caption reels easily because audio and captions arrive to the Writer in sequence. This is not so for video. */ @@ -112,9 +108,9 @@ Writer::Writer (shared_ptr film, weak_ptr j) void Writer::start () { - _thread = new boost::thread (boost::bind (&Writer::thread, this)); + _thread = boost::thread (boost::bind(&Writer::thread, this)); #ifdef DCPOMATIC_LINUX - pthread_setname_np (_thread->native_handle(), "writer"); + pthread_setname_np (_thread.native_handle(), "writer"); #endif } @@ -220,20 +216,18 @@ Writer::fake_write (Frame frame, Eyes eyes) } size_t const reel = video_reel (frame); - Frame const reel_frame = frame - _reels[reel].start (); - - FILE* file = fopen_boost (_film->info_file(_reels[reel].period()), "rb"); - if (!file) { - throw ReadFileError (_film->info_file(_reels[reel].period())); - } - dcp::FrameInfo info = _reels[reel].read_frame_info (file, reel_frame, eyes); - fclose (file); + Frame const frame_in_reel = frame - _reels[reel].start (); QueueItem qi; qi.type = QueueItem::FAKE; - qi.size = info.size; + + { + shared_ptr info_file = _film->info_file_handle(_reels[reel].period(), true); + qi.size = _reels[reel].read_frame_info(info_file, frame_in_reel, eyes).size; + } + qi.reel = reel; - qi.frame = reel_frame; + qi.frame = frame_in_reel; if (_film->three_d() && eyes == EYES_BOTH) { qi.eyes = EYES_LEFT; _queue.push_back (qi); @@ -276,8 +270,11 @@ Writer::write (shared_ptr audio, DCPTime const time) /* Easy case: we can write all the audio to this reel */ _audio_reel->write (audio); t = end; + } else if (_audio_reel->period().to <= t) { + /* This reel is entirely before the start of our audio; just skip the reel */ + ++_audio_reel; } else { - /* Split the audio into two and write the first part */ + /* This audio is over a reel boundary; split the audio into two and write the first part */ DCPTime part_lengths[2] = { _audio_reel->period().to - t, end - _audio_reel->period().to @@ -289,15 +286,12 @@ Writer::write (shared_ptr audio, DCPTime const time) }; if (part_frames[0]) { - shared_ptr part (new AudioBuffers (audio->channels(), part_frames[0])); - part->copy_from (audio.get(), part_frames[0], 0, 0); + shared_ptr part (new AudioBuffers(audio, part_frames[0], 0)); _audio_reel->write (part); } if (part_frames[1]) { - shared_ptr part (new AudioBuffers (audio->channels(), part_frames[1])); - part->copy_from (audio.get(), part_frames[1], part_frames[0], 0); - audio = part; + audio.reset (new AudioBuffers(audio, part_frames[1], part_frames[0])); } else { audio.reset (); } @@ -308,7 +302,8 @@ Writer::write (shared_ptr audio, DCPTime const time) } } -/** This must be called from Writer::thread() with an appropriate lock held */ + +/** Caller must hold a lock on _state_mutex */ bool Writer::have_sequenced_image_at_queue_head () { @@ -317,30 +312,41 @@ Writer::have_sequenced_image_at_queue_head () } _queue.sort (); - QueueItem const & f = _queue.front(); - ReelWriter const & reel = _reels[f.reel]; + return _last_written[f.reel].next(f); +} - /* The queue should contain only EYES_LEFT/EYES_RIGHT pairs or EYES_BOTH */ - if (f.eyes == EYES_BOTH) { +bool +Writer::LastWritten::next (QueueItem qi) const +{ + if (qi.eyes == EYES_BOTH) { /* 2D */ - return f.frame == (reel.last_written_video_frame() + 1); + return qi.frame == (_frame + 1); } /* 3D */ - if (reel.last_written_eyes() == EYES_LEFT && f.frame == reel.last_written_video_frame() && f.eyes == EYES_RIGHT) { + if (_eyes == EYES_LEFT && qi.frame == _frame && qi.eyes == EYES_RIGHT) { return true; } - if (reel.last_written_eyes() == EYES_RIGHT && f.frame == (reel.last_written_video_frame() + 1) && f.eyes == EYES_LEFT) { + if (_eyes == EYES_RIGHT && qi.frame == (_frame + 1) && qi.eyes == EYES_LEFT) { return true; } return false; } + +void +Writer::LastWritten::update (QueueItem qi) +{ + _frame = qi.frame; + _eyes = qi.eyes; +} + + void Writer::thread () try @@ -375,11 +381,11 @@ try /* (Hopefully temporarily) log anything that was not written */ if (!_queue.empty() && !have_sequenced_image_at_queue_head()) { LOG_WARNING (N_("Finishing writer with a left-over queue of %1:"), _queue.size()); - for (list::const_iterator i = _queue.begin(); i != _queue.end(); ++i) { - if (i->type == QueueItem::FULL) { - LOG_WARNING (N_("- type FULL, frame %1, eyes %2"), i->frame, (int) i->eyes); + BOOST_FOREACH (QueueItem const& i, _queue) { + if (i.type == QueueItem::FULL) { + LOG_WARNING (N_("- type FULL, frame %1, eyes %2"), i.frame, (int) i.eyes); } else { - LOG_WARNING (N_("- type FAKE, size %1, frame %2, eyes %3"), i->size, i->frame, (int) i->eyes); + LOG_WARNING (N_("- type FAKE, size %1, frame %2, eyes %3"), i.size, i.frame, (int) i.eyes); } } } @@ -389,6 +395,7 @@ try /* Write any frames that we can write; i.e. those that are in sequence. */ while (have_sequenced_image_at_queue_head ()) { QueueItem qi = _queue.front (); + _last_written[qi.reel].update (qi); _queue.pop_front (); if (qi.type == QueueItem::FULL && qi.encoded) { --_queued_full_in_memory; @@ -409,7 +416,7 @@ try break; case QueueItem::FAKE: LOG_DEBUG_ENCODE (N_("Writer FAKE-writes %1"), qi.frame); - reel.fake_write (qi.frame, qi.eyes, qi.size); + reel.fake_write (qi.size); ++_fake_written; break; case QueueItem::REPEAT: @@ -438,7 +445,7 @@ try DCPOMATIC_ASSERT (i != _queue.rend()); ++_pushed_to_disk; /* For the log message below */ - int const awaiting = _reels[_queue.front().reel].last_written_video_frame() + 1; + int const awaiting = _last_written[_queue.front().reel].frame() + 1; lock.unlock (); /* i is valid here, even though we don't hold a lock on the mutex, @@ -469,7 +476,7 @@ void Writer::terminate_thread (bool can_throw) { boost::mutex::scoped_lock lock (_state_mutex); - if (_thread == 0) { + if (!_thread.joinable()) { return; } @@ -478,22 +485,17 @@ Writer::terminate_thread (bool can_throw) _full_condition.notify_all (); lock.unlock (); - if (_thread->joinable ()) { - _thread->join (); - } + _thread.join (); if (can_throw) { rethrow (); } - - delete _thread; - _thread = 0; } void Writer::finish () { - if (!_thread) { + if (!_thread.joinable()) { return; } @@ -564,6 +566,8 @@ Writer::finish () meta.set_issue_date_now (); cpl->set_metadata (meta); + cpl->set_ratings (vector_to_list(_film->ratings())); + cpl->set_content_version_label_text (_film->content_version()); shared_ptr signer; if (_film->is_signed ()) { @@ -590,7 +594,7 @@ Writer::write_cover_sheet () boost::filesystem::path const cover = _film->file ("COVER_SHEET.txt"); FILE* f = fopen_boost (cover, "w"); if (!f) { - throw OpenFileError (cover, errno, false); + throw OpenFileError (cover, errno, OpenFileError::WRITE); } string text = Config::instance()->cover_sheet (); @@ -598,7 +602,16 @@ Writer::write_cover_sheet () boost::algorithm::replace_all (text, "$TYPE", _film->dcp_content_type()->pretty_name()); boost::algorithm::replace_all (text, "$CONTAINER", _film->container()->container_nickname()); boost::algorithm::replace_all (text, "$AUDIO_LANGUAGE", _film->isdcf_metadata().audio_language); - boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", _film->isdcf_metadata().subtitle_language); + + optional subtitle_language; + BOOST_FOREACH (shared_ptr i, _film->content()) { + BOOST_FOREACH (shared_ptr j, i->text) { + if (j->type() == TEXT_OPEN_SUBTITLE && j->use()) { + subtitle_language = j->language (); + } + } + } + boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", subtitle_language.get_value_or("None")); boost::uintmax_t size = 0; for ( @@ -641,7 +654,7 @@ Writer::write_cover_sheet () boost::algorithm::replace_all (text, "$LENGTH", length); - fwrite (text.c_str(), 1, text.length(), f); + checked_fwrite (text.c_str(), text.length(), f, cover); fclose (f); } @@ -686,7 +699,6 @@ Writer::write (PlayerText text, TextType type, optional track, DCP DCPOMATIC_ASSERT (false); } - DCPOMATIC_ASSERT (*reel != _reels.end()); while ((*reel)->period().to <= period.from) { ++(*reel); @@ -765,14 +777,16 @@ Writer::video_reel (int frame) const void Writer::set_digest_progress (Job* job, float progress) { - /* I believe this is thread-safe */ - _digest_progresses[boost::this_thread::get_id()] = progress; - boost::mutex::scoped_lock lm (_digest_progresses_mutex); + + _digest_progresses[boost::this_thread::get_id()] = progress; float min_progress = FLT_MAX; for (map::const_iterator i = _digest_progresses.begin(); i != _digest_progresses.end(); ++i) { min_progress = min (min_progress, i->second); } job->set_progress (min_progress); + + Waker waker; + waker.nudge (); }