+ return a.reel == b.reel && a.frame == b.frame && a.eyes == b.eyes;
+}
+
+
+void
+Writer::set_encoder_threads (int threads)
+{
+ boost::mutex::scoped_lock lm (_state_mutex);
+ _maximum_frames_in_memory = lrint (threads * Config::instance()->frames_in_memory_multiplier());
+ _maximum_queue_size = threads * 16;
+}
+
+
+void
+Writer::write (ReferencedReelAsset asset)
+{
+ _reel_assets.push_back (asset);
+}
+
+
+size_t
+Writer::video_reel (int frame) const
+{
+ auto t = DCPTime::from_frames (frame, film()->video_frame_rate());
+ size_t i = 0;
+ while (i < _reels.size() && !_reels[i].period().contains (t)) {
+ ++i;
+ }
+
+ DCPOMATIC_ASSERT (i < _reels.size ());
+ return i;
+}
+
+
+void
+Writer::set_digest_progress (Job* job, float progress)
+{
+ boost::mutex::scoped_lock lm (_digest_progresses_mutex);
+
+ _digest_progresses[boost::this_thread::get_id()] = progress;
+ float min_progress = FLT_MAX;
+ for (auto const& i: _digest_progresses) {
+ min_progress = min (min_progress, i.second);
+ }
+
+ job->set_progress (min_progress);
+
+ Waker waker;
+ waker.nudge ();
+
+ boost::this_thread::interruption_point();
+}
+
+
+/** Calculate hashes for any referenced MXF assets which do not already have one */
+void
+Writer::calculate_referenced_digests (std::function<void (float)> set_progress)
+try
+{
+ for (auto const& i: _reel_assets) {
+ auto file = dynamic_pointer_cast<dcp::ReelFileAsset>(i.asset);
+ if (file && !file->hash()) {
+ file->asset_ref().asset()->hash (set_progress);
+ file->set_hash (file->asset_ref().asset()->hash());
+ }
+ }
+} catch (boost::thread_interrupted) {
+ /* set_progress contains an interruption_point, so any of these methods
+ * may throw thread_interrupted, at which point we just give up.
+ */
+}
+
+
+void
+Writer::write_hanging_text (ReelWriter& reel)
+{
+ vector<HangingText> new_hanging_texts;
+ for (auto i: _hanging_texts) {
+ if (i.period.from == reel.period().from) {
+ reel.write (i.text, i.type, i.track, i.period, _fonts);
+ } else {
+ new_hanging_texts.push_back (i);
+ }
+ }
+ _hanging_texts = new_hanging_texts;