X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fwriter.cc;h=7d1088276995a242300402684facbf0290e3876f;hb=ca981c8cfa23111e92be329f1c2dfbe3a07b4247;hp=79e5c8c3695dccf9e23d3726889a267004509725;hpb=14b8b29e6660f5a7fb21135fb5f90b4c1ce51a4b;p=dcpomatic.git diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 79e5c8c36..7d1088276 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -63,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 */ @@ -87,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. */ @@ -95,10 +97,11 @@ Writer::Writer (shared_ptr film, weak_ptr j) BOOST_FOREACH (DCPTextTrack i, _film->closed_caption_tracks()) { _caption_reels[i] = _reels.begin (); } + _atmos_reel = _reels.begin (); - /* Check that the signer is OK if we need one */ + /* Check that the signer is OK */ string reason; - if (_film->is_signed() && !Config::instance()->signer_chain()->valid(&reason)) { + if (!Config::instance()->signer_chain()->valid(&reason)) { throw InvalidSignerError (reason); } } @@ -106,9 +109,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 } @@ -214,20 +217,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); @@ -270,8 +271,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 @@ -283,15 +287,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 (); } @@ -302,7 +303,21 @@ Writer::write (shared_ptr audio, DCPTime const time) } } -/** This must be called from Writer::thread() with an appropriate lock held */ + +void +Writer::write (shared_ptr atmos, DCPTime time, AtmosMetadata metadata) +{ + if (_atmos_reel->period().to == time) { + ++_atmos_reel; + DCPOMATIC_ASSERT (_atmos_reel != _reels.end()); + } + + /* We assume that we get a video frame's worth of data here */ + _atmos_reel->write (atmos, metadata); +} + + +/** Caller must hold a lock on _state_mutex */ bool Writer::have_sequenced_image_at_queue_head () { @@ -311,30 +326,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 @@ -369,11 +395,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); } } } @@ -383,6 +409,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; @@ -403,7 +430,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: @@ -432,7 +459,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, @@ -463,7 +490,7 @@ void Writer::terminate_thread (bool can_throw) { boost::mutex::scoped_lock lock (_state_mutex); - if (_thread == 0) { + if (!_thread.joinable()) { return; } @@ -472,22 +499,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; } @@ -559,15 +581,14 @@ Writer::finish () 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 ()) { - signer = Config::instance()->signer_chain (); - /* We did check earlier, but check again here to be on the safe side */ - string reason; - if (!signer->valid (&reason)) { - throw InvalidSignerError (reason); - } + signer = Config::instance()->signer_chain (); + /* We did check earlier, but check again here to be on the safe side */ + string reason; + if (!signer->valid (&reason)) { + throw InvalidSignerError (reason); } dcp.write_xml (_film->interop () ? dcp::INTEROP : dcp::SMPTE, meta, signer, Config::instance()->dcp_metadata_filename_format()); @@ -585,7 +606,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 (); @@ -768,14 +789,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 (); }