X-Git-Url: https://main.carlh.net/gitweb/?p=dcpomatic.git;a=blobdiff_plain;f=src%2Flib%2Fwriter.cc;h=d346c4a4ff32eba5573ffe6c897f29301a02f101;hp=c0a774f03584a2c0e859fdb84aec802c450132cb;hb=8f12e84009d7c2685bb2eeb32665876463d4e6e5;hpb=27b83475435dda4e84a90cf59a52f150905c4ab1 diff --git a/src/lib/writer.cc b/src/lib/writer.cc index c0a774f03..d346c4a4f 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2017 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 @@ -63,15 +57,17 @@ using std::cout; using std::map; using std::min; using std::max; +using std::vector; using boost::shared_ptr; 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 */ @@ -91,12 +87,15 @@ 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. */ _audio_reel = _reels.begin (); - for (int i = 0; i < CAPTION_COUNT; ++i) { - _caption_reel[i] = _reels.begin (); + _subtitle_reel = _reels.begin (); + BOOST_FOREACH (DCPTextTrack i, _film->closed_caption_tracks()) { + _caption_reels[i] = _reels.begin (); } /* Check that the signer is OK if we need one */ @@ -109,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 } @@ -217,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); @@ -273,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 @@ -286,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 (); } @@ -305,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 () { @@ -314,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 @@ -372,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); } } } @@ -386,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; @@ -406,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: @@ -435,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(); + 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, @@ -466,7 +476,7 @@ void Writer::terminate_thread (bool can_throw) { boost::mutex::scoped_lock lock (_state_mutex); - if (_thread == 0) { + if (!_thread.joinable()) { return; } @@ -475,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; } @@ -561,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 ()) { @@ -587,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 (); @@ -595,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 ( @@ -638,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); } @@ -664,17 +680,32 @@ Writer::can_fake_write (Frame frame) const return (frame != 0 && frame < reel.first_nonexistant_frame()); } +/** @param track Closed caption track if type == TEXT_CLOSED_CAPTION */ void -Writer::write (PlayerCaption text, CaptionType type, DCPTimePeriod period) +Writer::write (PlayerText text, TextType type, optional track, DCPTimePeriod period) { - while (_caption_reel[type]->period().to <= period.from) { - ++_caption_reel[type]; - DCPOMATIC_ASSERT (_caption_reel[type] != _reels.end()); + vector::iterator* reel = 0; + + switch (type) { + case TEXT_OPEN_SUBTITLE: + reel = &_subtitle_reel; + break; + case TEXT_CLOSED_CAPTION: + DCPOMATIC_ASSERT (track); + DCPOMATIC_ASSERT (_caption_reels.find(*track) != _caption_reels.end()); + reel = &_caption_reels[*track]; + break; + default: + DCPOMATIC_ASSERT (false); } - DCPOMATIC_ASSERT (_caption_reel[type] != _reels.end()); + DCPOMATIC_ASSERT (*reel != _reels.end()); + while ((*reel)->period().to <= period.from) { + ++(*reel); + DCPOMATIC_ASSERT (*reel != _reels.end()); + } - _caption_reel[type]->write (text, type, period); + (*reel)->write (text, type, track, period); } void @@ -756,4 +787,7 @@ Writer::set_digest_progress (Job* job, float progress) } job->set_progress (min_progress); + + Waker waker; + waker.nudge (); }